]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/hostapd/eap.c
This commit was generated by cvs2svn to compensate for changes in r147894,
[FreeBSD/FreeBSD.git] / contrib / hostapd / eap.c
1 /*
2  * hostapd / EAP Standalone Authenticator state machine
3  * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
4  *
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.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  *
14  * $FreeBSD$
15  */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <netinet/in.h>
21 #include <string.h>
22 #include <sys/socket.h>
23
24 #include "hostapd.h"
25 #include "eloop.h"
26 #include "sta_info.h"
27 #include "eap_i.h"
28
29
30 extern const struct eap_method eap_method_identity;
31 #ifdef EAP_MD5
32 extern const struct eap_method eap_method_md5;
33 #endif /* EAP_MD5 */
34 #ifdef EAP_TLS
35 extern const struct eap_method eap_method_tls;
36 #endif /* EAP_TLS */
37 #ifdef EAP_MSCHAPv2
38 extern const struct eap_method eap_method_mschapv2;
39 #endif /* EAP_MSCHAPv2 */
40 #ifdef EAP_PEAP
41 extern const struct eap_method eap_method_peap;
42 #endif /* EAP_PEAP */
43 #ifdef EAP_TLV
44 extern const struct eap_method eap_method_tlv;
45 #endif /* EAP_TLV */
46 #ifdef EAP_GTC
47 extern const struct eap_method eap_method_gtc;
48 #endif /* EAP_GTC */
49 #ifdef EAP_TTLS
50 extern const struct eap_method eap_method_ttls;
51 #endif /* EAP_TTLS */
52 #ifdef EAP_SIM
53 extern const struct eap_method eap_method_sim;
54 #endif /* EAP_SIM */
55
56 static const struct eap_method *eap_methods[] =
57 {
58         &eap_method_identity,
59 #ifdef EAP_MD5
60         &eap_method_md5,
61 #endif /* EAP_MD5 */
62 #ifdef EAP_TLS
63         &eap_method_tls,
64 #endif /* EAP_TLS */
65 #ifdef EAP_MSCHAPv2
66         &eap_method_mschapv2,
67 #endif /* EAP_MSCHAPv2 */
68 #ifdef EAP_PEAP
69         &eap_method_peap,
70 #endif /* EAP_PEAP */
71 #ifdef EAP_TTLS
72         &eap_method_ttls,
73 #endif /* EAP_TTLS */
74 #ifdef EAP_TLV
75         &eap_method_tlv,
76 #endif /* EAP_TLV */
77 #ifdef EAP_GTC
78         &eap_method_gtc,
79 #endif /* EAP_GTC */
80 #ifdef EAP_SIM
81         &eap_method_sim,
82 #endif /* EAP_SIM */
83 };
84 #define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0]))
85
86
87 const struct eap_method * eap_sm_get_eap_methods(int method)
88 {
89         int i;
90         for (i = 0; i < NUM_EAP_METHODS; i++) {
91                 if (eap_methods[i]->method == method)
92                         return eap_methods[i];
93         }
94         return NULL;
95 }
96
97 static void eap_user_free(struct eap_user *user);
98
99
100 /* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */
101
102 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
103                                    int eapSRTT, int eapRTTVAR,
104                                    int methodTimeout);
105 static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len);
106 static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len);
107 static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len);
108 static int eap_sm_nextId(struct eap_sm *sm, int id);
109 static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len);
110 static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm);
111 static int eap_sm_Policy_getDecision(struct eap_sm *sm);
112 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
113
114
115 /* Definitions for clarifying state machine implementation */
116 #define SM_STATE(machine, state) \
117 static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \
118         int global)
119
120 #define SM_ENTRY(machine, state) \
121 if (!global || sm->machine ## _state != machine ## _ ## state) { \
122         sm->changed = TRUE; \
123         wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \
124 } \
125 sm->machine ## _state = machine ## _ ## state;
126
127 #define SM_ENTER(machine, state) \
128 sm_ ## machine ## _ ## state ## _Enter(sm, 0)
129 #define SM_ENTER_GLOBAL(machine, state) \
130 sm_ ## machine ## _ ## state ## _Enter(sm, 1)
131
132 #define SM_STEP(machine) \
133 static void sm_ ## machine ## _Step(struct eap_sm *sm)
134
135 #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
136
137
138 static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
139 {
140         return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
141 }
142
143
144 static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
145                            Boolean value)
146 {
147         sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
148 }
149
150
151 static void eapol_set_eapReqData(struct eap_sm *sm,
152                                  const u8 *eapReqData, size_t eapReqDataLen)
153 {
154         wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL",
155                     sm->eapReqData, sm->eapReqDataLen);
156         sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen);
157 }
158
159
160 static void eapol_set_eapKeyData(struct eap_sm *sm,
161                                  const u8 *eapKeyData, size_t eapKeyDataLen)
162 {
163         wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL",
164                     sm->eapKeyData, sm->eapKeyDataLen);
165         sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen);
166 }
167
168
169 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
170                  int phase2)
171 {
172         struct eap_user *user;
173
174         if (sm == NULL || sm->eapol_cb == NULL ||
175             sm->eapol_cb->get_eap_user == NULL)
176                 return -1;
177
178         eap_user_free(sm->user);
179         sm->user = NULL;
180
181         user = malloc(sizeof(*user));
182         if (user == NULL)
183             return -1;
184         memset(user, 0, sizeof(*user));
185
186         if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
187                                        identity_len, phase2, user) != 0) {
188                 eap_user_free(user);
189                 return -1;
190         }
191
192         sm->user = user;
193         sm->user_eap_method_index = 0;
194
195         return 0;
196 }
197
198
199 SM_STATE(EAP, DISABLED)
200 {
201         SM_ENTRY(EAP, DISABLED);
202 }
203
204
205 SM_STATE(EAP, INITIALIZE)
206 {
207         SM_ENTRY(EAP, INITIALIZE);
208
209         sm->currentId = -1;
210         eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
211         eapol_set_bool(sm, EAPOL_eapFail, FALSE);
212         eapol_set_bool(sm, EAPOL_eapTimeout, FALSE);
213         free(sm->eapKeyData);
214         sm->eapKeyData = NULL;
215         sm->eapKeyDataLen = 0;
216         /* eapKeyAvailable = FALSE */
217         eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
218
219         /* This is not defined in draft-ietf-eap-statemachine-05.txt, but
220          * method state needs to be reseted here so that it does not remain in
221          * success state when re-authentication starts. */
222         if (sm->m && sm->eap_method_priv) {
223                 sm->m->reset(sm, sm->eap_method_priv);
224                 sm->eap_method_priv = NULL;
225         }
226         sm->m = NULL;
227         sm->user_eap_method_index = 0;
228
229         if (sm->backend_auth) {
230                 sm->currentMethod = EAP_TYPE_NONE;
231                 /* parse rxResp, respId, respMethod */
232                 eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
233                 if (sm->rxResp) {
234                         sm->currentId = sm->respId;
235                 }
236         }
237 }
238
239
240 SM_STATE(EAP, PICK_UP_METHOD)
241 {
242         SM_ENTRY(EAP, PICK_UP_METHOD);
243
244         if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
245                 sm->currentMethod = sm->respMethod;
246                 if (sm->m && sm->eap_method_priv) {
247                         sm->m->reset(sm, sm->eap_method_priv);
248                         sm->eap_method_priv = NULL;
249                 }
250                 sm->m = eap_sm_get_eap_methods(sm->currentMethod);
251                 if (sm->m && sm->m->initPickUp) {
252                         sm->eap_method_priv = sm->m->initPickUp(sm);
253                         if (sm->eap_method_priv == NULL) {
254                                 wpa_printf(MSG_DEBUG, "EAP: Failed to "
255                                            "initialize EAP method %d",
256                                            sm->currentMethod);
257                                 sm->m = NULL;
258                                 sm->currentMethod = EAP_TYPE_NONE;
259                         }
260                 } else {
261                         sm->m = NULL;
262                         sm->currentMethod = EAP_TYPE_NONE;
263                 }
264         }
265 }
266
267
268 SM_STATE(EAP, IDLE)
269 {
270         SM_ENTRY(EAP, IDLE);
271
272         sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount,
273                                                    sm->eapSRTT, sm->eapRTTVAR,
274                                                    sm->methodTimeout);
275 }
276
277
278 SM_STATE(EAP, RETRANSMIT)
279 {
280         SM_ENTRY(EAP, RETRANSMIT);
281
282         /* TODO: Is this needed since EAPOL state machines take care of
283          * retransmit? */
284 }
285
286
287 SM_STATE(EAP, RECEIVED)
288 {
289         SM_ENTRY(EAP, RECEIVED);
290
291         /* parse rxResp, respId, respMethod */
292         eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
293 }
294
295
296 SM_STATE(EAP, DISCARD)
297 {
298         SM_ENTRY(EAP, DISCARD);
299         eapol_set_bool(sm, EAPOL_eapResp, FALSE);
300         eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
301 }
302
303
304 SM_STATE(EAP, SEND_REQUEST)
305 {
306         SM_ENTRY(EAP, SEND_REQUEST);
307
308         sm->retransCount = 0;
309         if (sm->eapReqData) {
310                 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
311                 free(sm->lastReqData);
312                 sm->lastReqData = sm->eapReqData;
313                 sm->lastReqDataLen = sm->eapReqDataLen;
314                 sm->eapReqData = NULL;
315                 sm->eapReqDataLen = 0;
316                 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
317                 eapol_set_bool(sm, EAPOL_eapReq, TRUE);
318         } else {
319                 wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
320                 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
321                 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
322                 eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
323         }
324 }
325
326
327 SM_STATE(EAP, INTEGRITY_CHECK)
328 {
329         SM_ENTRY(EAP, INTEGRITY_CHECK);
330
331         if (sm->m->check) {
332                 sm->ignore = sm->m->check(sm, sm->eap_method_priv,
333                                           sm->eapRespData, sm->eapRespDataLen);
334         }
335 }
336
337
338 SM_STATE(EAP, METHOD_REQUEST)
339 {
340         SM_ENTRY(EAP, METHOD_REQUEST);
341
342         if (sm->m == NULL) {
343                 wpa_printf(MSG_DEBUG, "EAP: method not initialized");
344                 return;
345         }
346
347         sm->currentId = eap_sm_nextId(sm, sm->currentId);
348         wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
349                    sm->currentId);
350         sm->lastId = sm->currentId;
351         free(sm->eapReqData);
352         sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
353                                          sm->currentId, &sm->eapReqDataLen);
354         if (sm->m->getTimeout)
355                 sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
356         else
357                 sm->methodTimeout = 0;
358 }
359
360
361 SM_STATE(EAP, METHOD_RESPONSE)
362 {
363         SM_ENTRY(EAP, METHOD_RESPONSE);
364
365         sm->m->process(sm, sm->eap_method_priv, sm->eapRespData,
366                        sm->eapRespDataLen);
367         if (sm->m->isDone(sm, sm->eap_method_priv)) {
368                 eap_sm_Policy_update(sm, NULL, 0);
369                 free(sm->eapKeyData);
370                 if (sm->m->getKey) {
371                         sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
372                                                        &sm->eapKeyDataLen);
373                 } else {
374                         sm->eapKeyData = NULL;
375                         sm->eapKeyDataLen = 0;
376                 }
377                 sm->methodState = METHOD_END;
378         } else {
379                 sm->methodState = METHOD_CONTINUE;
380         }
381 }
382
383
384 SM_STATE(EAP, PROPOSE_METHOD)
385 {
386         SM_ENTRY(EAP, PROPOSE_METHOD);
387
388         sm->currentMethod = eap_sm_Policy_getNextMethod(sm);
389         if (sm->m && sm->eap_method_priv) {
390                 sm->m->reset(sm, sm->eap_method_priv);
391                 sm->eap_method_priv = NULL;
392         }
393         sm->m = eap_sm_get_eap_methods(sm->currentMethod);
394         if (sm->m) {
395                 sm->eap_method_priv = sm->m->init(sm);
396                 if (sm->eap_method_priv == NULL) {
397                         wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
398                                    "method %d", sm->currentMethod);
399                         sm->m = NULL;
400                         sm->currentMethod = EAP_TYPE_NONE;
401                 }
402         }
403         if (sm->currentMethod == EAP_TYPE_IDENTITY ||
404             sm->currentMethod == EAP_TYPE_NOTIFICATION)
405                 sm->methodState = METHOD_CONTINUE;
406         else
407                 sm->methodState = METHOD_PROPOSED;
408 }
409
410
411 SM_STATE(EAP, NAK)
412 {
413         struct eap_hdr *nak;
414         size_t len = 0;
415         u8 *pos, *nak_list = NULL;
416
417         SM_ENTRY(EAP, NAK);
418
419         if (sm->eap_method_priv) {
420                 sm->m->reset(sm, sm->eap_method_priv);
421                 sm->eap_method_priv = NULL;
422         }
423         sm->m = NULL;
424
425         nak = (struct eap_hdr *) sm->eapRespData;
426         if (nak && sm->eapRespDataLen > sizeof(*nak)) {
427                 len = ntohs(nak->length);
428                 if (len > sm->eapRespDataLen)
429                         len = sm->eapRespDataLen;
430                 pos = (u8 *) (nak + 1);
431                 len -= sizeof(*nak);
432                 if (*pos == EAP_TYPE_NAK) {
433                         pos++;
434                         len--;
435                         nak_list = pos;
436                 }
437         }
438         eap_sm_Policy_update(sm, nak_list, len);
439 }
440
441
442 SM_STATE(EAP, SELECT_ACTION)
443 {
444         SM_ENTRY(EAP, SELECT_ACTION);
445
446         sm->decision = eap_sm_Policy_getDecision(sm);
447 }
448
449
450 SM_STATE(EAP, TIMEOUT_FAILURE)
451 {
452         SM_ENTRY(EAP, TIMEOUT_FAILURE);
453
454         eapol_set_bool(sm, EAPOL_eapTimeout, TRUE);
455 }
456
457
458 SM_STATE(EAP, FAILURE)
459 {
460         SM_ENTRY(EAP, FAILURE);
461
462         free(sm->eapReqData);
463         sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId,
464                                              &sm->eapReqDataLen);
465         if (sm->eapReqData) {
466                 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
467                 free(sm->eapReqData);
468                 sm->eapReqData = NULL;
469                 sm->eapReqDataLen = 0;
470         }
471         free(sm->lastReqData);
472         sm->lastReqData = NULL;
473         sm->lastReqDataLen = 0;
474         eapol_set_bool(sm, EAPOL_eapFail, TRUE);
475 }
476
477
478 SM_STATE(EAP, SUCCESS)
479 {
480         SM_ENTRY(EAP, SUCCESS);
481
482         free(sm->eapReqData);
483         sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId,
484                                              &sm->eapReqDataLen);
485         if (sm->eapReqData) {
486                 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
487                 free(sm->eapReqData);
488                 sm->eapReqData = NULL;
489                 sm->eapReqDataLen = 0;
490         }
491         free(sm->lastReqData);
492         sm->lastReqData = NULL;
493         sm->lastReqDataLen = 0;
494         if (sm->eapKeyData) {
495                 eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen);
496         }
497         eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
498 }
499
500
501 SM_STEP(EAP)
502 {
503         if (eapol_get_bool(sm, EAPOL_eapRestart) &&
504             eapol_get_bool(sm, EAPOL_portEnabled))
505                 SM_ENTER_GLOBAL(EAP, INITIALIZE);
506         else if (!eapol_get_bool(sm, EAPOL_portEnabled))
507                 SM_ENTER_GLOBAL(EAP, DISABLED);
508         else switch (sm->EAP_state) {
509         case EAP_INITIALIZE:
510                 if (sm->backend_auth) {
511                         if (!sm->rxResp)
512                                 SM_ENTER(EAP, SELECT_ACTION);
513                         else if (sm->rxResp &&
514                                  (sm->respMethod == EAP_TYPE_NAK ||
515                                   sm->respMethod == EAP_TYPE_EXPANDED_NAK))
516                                 SM_ENTER(EAP, NAK);
517                         else
518                                 SM_ENTER(EAP, PICK_UP_METHOD);
519                 } else {
520                         SM_ENTER(EAP, SELECT_ACTION);
521                 }
522                 break;
523         case EAP_PICK_UP_METHOD:
524                 if (sm->currentMethod == EAP_TYPE_NONE) {
525                         SM_ENTER(EAP, SELECT_ACTION);
526                 } else {
527                         SM_ENTER(EAP, METHOD_RESPONSE);
528                 }
529                 break;
530         case EAP_DISABLED:
531                 if (eapol_get_bool(sm, EAPOL_portEnabled))
532                         SM_ENTER(EAP, INITIALIZE);
533                 break;
534         case EAP_IDLE:
535                 if (sm->retransWhile == 0)
536                         SM_ENTER(EAP, RETRANSMIT);
537                 else if (eapol_get_bool(sm, EAPOL_eapResp))
538                         SM_ENTER(EAP, RECEIVED);
539                 break;
540         case EAP_RETRANSMIT:
541                 if (sm->retransCount > sm->MaxRetrans)
542                         SM_ENTER(EAP, TIMEOUT_FAILURE);
543                 else
544                         SM_ENTER(EAP, IDLE);
545                 break;
546         case EAP_RECEIVED:
547                 if (sm->rxResp && (sm->respId == sm->currentId) &&
548                     (sm->respMethod == EAP_TYPE_NAK ||
549                      sm->respMethod == EAP_TYPE_EXPANDED_NAK)
550                     && (sm->methodState == METHOD_PROPOSED))
551                         SM_ENTER(EAP, NAK);
552                 else if (sm->rxResp && (sm->respId == sm->currentId) &&
553                          (sm->respMethod == sm->currentMethod))
554                         SM_ENTER(EAP, INTEGRITY_CHECK);
555                 else
556                         SM_ENTER(EAP, DISCARD);
557                 break;
558         case EAP_DISCARD:
559                 SM_ENTER(EAP, IDLE);
560                 break;
561         case EAP_SEND_REQUEST:
562                 SM_ENTER(EAP, IDLE);
563                 break;
564         case EAP_INTEGRITY_CHECK:
565                 if (sm->ignore)
566                         SM_ENTER(EAP, DISCARD);
567                 else
568                         SM_ENTER(EAP, METHOD_RESPONSE);
569                 break;
570         case EAP_METHOD_REQUEST:
571                 SM_ENTER(EAP, SEND_REQUEST);
572                 break;
573         case EAP_METHOD_RESPONSE:
574                 if (sm->methodState == METHOD_END)
575                         SM_ENTER(EAP, SELECT_ACTION);
576                 else
577                         SM_ENTER(EAP, METHOD_REQUEST);
578                 break;
579         case EAP_PROPOSE_METHOD:
580                 SM_ENTER(EAP, METHOD_REQUEST);
581                 break;
582         case EAP_NAK:
583                 SM_ENTER(EAP, SELECT_ACTION);
584                 break;
585         case EAP_SELECT_ACTION:
586                 if (sm->decision == DECISION_FAILURE)
587                         SM_ENTER(EAP, FAILURE);
588                 else if (sm->decision == DECISION_SUCCESS)
589                         SM_ENTER(EAP, SUCCESS);
590                 else
591                         SM_ENTER(EAP, PROPOSE_METHOD);
592                 break;
593         case EAP_TIMEOUT_FAILURE:
594                 break;
595         case EAP_FAILURE:
596                 break;
597         case EAP_SUCCESS:
598                 break;
599         }
600 }
601
602
603 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
604                                    int eapSRTT, int eapRTTVAR,
605                                    int methodTimeout)
606 {
607         /* For now, retransmission is done in EAPOL state machines, so make
608          * sure EAP state machine does not end up trying to retransmit packets.
609          */
610         return 1;
611 }
612
613
614 static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len)
615 {
616         struct eap_hdr *hdr;
617         size_t plen;
618
619         /* parse rxResp, respId, respMethod */
620         sm->rxResp = FALSE;
621         sm->respId = -1;
622         sm->respMethod = EAP_TYPE_NONE;
623
624         if (resp == NULL || len < sizeof(*hdr))
625                 return;
626
627         hdr = (struct eap_hdr *) resp;
628         plen = ntohs(hdr->length);
629         if (plen > len) {
630                 wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
631                            "(len=%lu plen=%lu)", (unsigned long) len,
632                            (unsigned long) plen);
633                 return;
634         }
635
636         sm->respId = hdr->identifier;
637
638         if (hdr->code == EAP_CODE_RESPONSE)
639                 sm->rxResp = TRUE;
640
641         if (len > sizeof(*hdr))
642                 sm->respMethod = *((u8 *) (hdr + 1));
643
644         wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
645                    "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod);
646 }
647
648
649 static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len)
650 {
651         struct eap_hdr *resp;
652         wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
653
654         *len = sizeof(*resp);
655         resp = malloc(*len);
656         if (resp == NULL)
657                 return NULL;
658         resp->code = EAP_CODE_SUCCESS;
659         resp->identifier = id;
660         resp->length = htons(*len);
661
662         return (u8 *) resp;
663 }
664
665
666 static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len)
667 {
668         struct eap_hdr *resp;
669         wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
670
671         *len = sizeof(*resp);
672         resp = malloc(*len);
673         if (resp == NULL)
674                 return NULL;
675         resp->code = EAP_CODE_FAILURE;
676         resp->identifier = id;
677         resp->length = htons(*len);
678
679         return (u8 *) resp;
680 }
681
682
683 static int eap_sm_nextId(struct eap_sm *sm, int id)
684 {
685         if (id < 0) {
686                 /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a
687                  * random number */
688                 id = rand() & 0xff;
689                 if (id != sm->lastId)
690                         return id;
691         }
692         return (id + 1) & 0xff;
693 }
694
695
696 void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len)
697 {
698         int i, j;
699
700         wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
701                    "index %d)", sm->user_eap_method_index);
702
703         wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
704                     sm->user->methods, EAP_MAX_METHODS);
705         wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
706                     nak_list, len);
707
708         i = sm->user_eap_method_index;
709         while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) {
710                 for (j = 0; j < len; j++) {
711                         if (nak_list[j] == sm->user->methods[i]) {
712                                 break;
713                         }
714                 }
715
716                 if (j < len) {
717                         /* found */
718                         i++;
719                         continue;
720                 }
721
722                 /* not found - remove from the list */
723                 memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
724                         EAP_MAX_METHODS - i - 1);
725                 sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE;
726         }
727
728         wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
729                     sm->user->methods, EAP_MAX_METHODS);
730 }
731
732
733 static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len)
734 {
735         if (nak_list == NULL || sm == NULL || sm->user == NULL)
736                 return;
737
738         if (sm->user->phase2) {
739                 wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
740                            " info was selected - reject");
741                 sm->decision = DECISION_FAILURE;
742                 return;
743         }
744
745         eap_sm_process_nak(sm, nak_list, len);
746 }
747
748
749 static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm)
750 {
751         EapType next;
752
753         /* In theory, there should be no problems with starting
754          * re-authentication with something else than EAP-Request/Identity and
755          * this does indeed work with wpa_supplicant. However, at least Funk
756          * Supplicant seemed to ignore re-auth if it skipped
757          * EAP-Request/Identity.
758          * Re-auth sets currentId == -1, so that can be used here to select
759          * whether Identity needs to be requested again. */
760         if (sm->identity == NULL || sm->currentId == -1) {
761                 next = EAP_TYPE_IDENTITY;
762                 sm->update_user = TRUE;
763         } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
764                    sm->user->methods[sm->user_eap_method_index] !=
765                    EAP_TYPE_NONE) {
766                 next = sm->user->methods[sm->user_eap_method_index++];
767         } else {
768                 next = EAP_TYPE_NONE;
769         }
770         wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next);
771         return next;
772 }
773
774
775 static int eap_sm_Policy_getDecision(struct eap_sm *sm)
776 {
777         if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
778             sm->m->isSuccess(sm, sm->eap_method_priv)) {
779                 wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
780                            "SUCCESS");
781                 sm->update_user = TRUE;
782                 return DECISION_SUCCESS;
783         }
784
785         if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
786             !sm->m->isSuccess(sm, sm->eap_method_priv)) {
787                 wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
788                            "FAILURE");
789                 sm->update_user = TRUE;
790                 return DECISION_FAILURE;
791         }
792
793         if ((sm->user == NULL || sm->update_user) && sm->identity) {
794                 if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
795                         wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
796                                    "found from database -> FAILURE");
797                         return DECISION_FAILURE;
798                 }
799                 sm->update_user = FALSE;
800         }
801
802         if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
803             sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) {
804                 wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
805                            "available -> CONTINUE");
806                 return DECISION_CONTINUE;
807         }
808
809         if (sm->identity == NULL || sm->currentId == -1) {
810                 wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
811                            "yet -> CONTINUE");
812                 return DECISION_CONTINUE;
813         }
814
815         wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
816                    "FAILURE");
817         return DECISION_FAILURE;
818 }
819
820
821 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
822 {
823         return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
824 }
825
826
827 int eap_sm_step(struct eap_sm *sm)
828 {
829         int res = 0;
830         do {
831                 sm->changed = FALSE;
832                 SM_STEP_RUN(EAP);
833                 if (sm->changed)
834                         res = 1;
835         } while (sm->changed);
836         return res;
837 }
838
839
840 u8 eap_get_type(const char *name)
841 {
842         int i;
843         for (i = 0; i < NUM_EAP_METHODS; i++) {
844                 if (strcmp(eap_methods[i]->name, name) == 0)
845                         return eap_methods[i]->method;
846         }
847         return EAP_TYPE_NONE;
848 }
849
850
851 void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData,
852                          size_t eapRespDataLen)
853 {
854         if (sm == NULL)
855                 return;
856         free(sm->eapRespData);
857         sm->eapRespData = malloc(eapRespDataLen);
858         if (sm->eapRespData == NULL)
859                 return;
860         memcpy(sm->eapRespData, eapRespData, eapRespDataLen);
861         sm->eapRespDataLen = eapRespDataLen;
862         wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received",
863                     eapRespData, eapRespDataLen);
864 }
865
866
867 static void eap_user_free(struct eap_user *user)
868 {
869         if (user == NULL)
870                 return;
871         free(user->password);
872         user->password = NULL;
873         free(user);
874 }
875
876
877 struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
878                             struct eap_config *eap_conf)
879 {
880         struct eap_sm *sm;
881
882         sm = malloc(sizeof(*sm));
883         if (sm == NULL)
884                 return NULL;
885         memset(sm, 0, sizeof(*sm));
886         sm->eapol_ctx = eapol_ctx;
887         sm->eapol_cb = eapol_cb;
888         sm->MaxRetrans = 10;
889         sm->ssl_ctx = eap_conf->ssl_ctx;
890         sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv;
891         sm->backend_auth = eap_conf->backend_auth;
892
893         wpa_printf(MSG_DEBUG, "EAP: State machine created");
894
895         return sm;
896 }
897
898
899 void eap_sm_deinit(struct eap_sm *sm)
900 {
901         if (sm == NULL)
902                 return;
903         wpa_printf(MSG_DEBUG, "EAP: State machine removed");
904         if (sm->m && sm->eap_method_priv)
905                 sm->m->reset(sm, sm->eap_method_priv);
906         free(sm->eapReqData);
907         free(sm->eapKeyData);
908         free(sm->lastReqData);
909         free(sm->eapRespData);
910         free(sm->identity);
911         eap_user_free(sm->user);
912         free(sm);
913 }
914
915 void eap_sm_notify_cached(struct eap_sm *sm)
916 {
917         if (sm == NULL)
918                 return;
919
920         sm->EAP_state = EAP_SUCCESS;
921 }