Line data Source code
1 : /*
2 : * hostapd / EAP Full Authenticator state machine (RFC 4137)
3 : * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4 : *
5 : * This software may be distributed under the terms of the BSD license.
6 : * See README for more details.
7 : *
8 : * This state machine is based on the full authenticator state machine defined
9 : * in RFC 4137. However, to support backend authentication in RADIUS
10 : * authentication server functionality, parts of backend authenticator (also
11 : * from RFC 4137) are mixed in. This functionality is enabled by setting
12 : * backend_auth configuration variable to TRUE.
13 : */
14 :
15 : #include "includes.h"
16 :
17 : #include "common.h"
18 : #include "eap_i.h"
19 : #include "state_machine.h"
20 : #include "common/wpa_ctrl.h"
21 :
22 : #define STATE_MACHINE_DATA struct eap_sm
23 : #define STATE_MACHINE_DEBUG_PREFIX "EAP"
24 :
25 : #define EAP_MAX_AUTH_ROUNDS 50
26 :
27 : static void eap_user_free(struct eap_user *user);
28 :
29 :
30 : /* EAP state machines are described in RFC 4137 */
31 :
32 : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
33 : int eapSRTT, int eapRTTVAR,
34 : int methodTimeout);
35 : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
36 : static int eap_sm_getId(const struct wpabuf *data);
37 : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
38 : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
39 : static int eap_sm_nextId(struct eap_sm *sm, int id);
40 : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
41 : size_t len);
42 : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
43 : static int eap_sm_Policy_getDecision(struct eap_sm *sm);
44 : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
45 :
46 :
47 5568 : static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
48 : {
49 5568 : if (src == NULL)
50 0 : return -1;
51 :
52 5568 : wpabuf_free(*dst);
53 5568 : *dst = wpabuf_dup(src);
54 5568 : return *dst ? 0 : -1;
55 : }
56 :
57 :
58 255 : static int eap_copy_data(u8 **dst, size_t *dst_len,
59 : const u8 *src, size_t src_len)
60 : {
61 255 : if (src == NULL)
62 0 : return -1;
63 :
64 255 : os_free(*dst);
65 255 : *dst = os_malloc(src_len);
66 255 : if (*dst) {
67 255 : os_memcpy(*dst, src, src_len);
68 255 : *dst_len = src_len;
69 255 : return 0;
70 : } else {
71 0 : *dst_len = 0;
72 0 : return -1;
73 : }
74 : }
75 :
76 : #define EAP_COPY(dst, src) \
77 : eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
78 :
79 :
80 : /**
81 : * eap_user_get - Fetch user information from the database
82 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
83 : * @identity: Identity (User-Name) of the user
84 : * @identity_len: Length of identity in bytes
85 : * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
86 : * Returns: 0 on success, or -1 on failure
87 : *
88 : * This function is used to fetch user information for EAP. The user will be
89 : * selected based on the specified identity. sm->user and
90 : * sm->user_eap_method_index are updated for the new user when a matching user
91 : * is found. sm->user can be used to get user information (e.g., password).
92 : */
93 142 : int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
94 : int phase2)
95 : {
96 : struct eap_user *user;
97 :
98 284 : if (sm == NULL || sm->eapol_cb == NULL ||
99 142 : sm->eapol_cb->get_eap_user == NULL)
100 0 : return -1;
101 :
102 142 : eap_user_free(sm->user);
103 142 : sm->user = NULL;
104 :
105 142 : user = os_zalloc(sizeof(*user));
106 142 : if (user == NULL)
107 0 : return -1;
108 :
109 142 : if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
110 : identity_len, phase2, user) != 0) {
111 1 : eap_user_free(user);
112 1 : return -1;
113 : }
114 :
115 141 : sm->user = user;
116 141 : sm->user_eap_method_index = 0;
117 :
118 141 : return 0;
119 : }
120 :
121 :
122 1028 : void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
123 : {
124 : va_list ap;
125 : char *buf;
126 : int buflen;
127 :
128 1028 : if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
129 2046 : return;
130 :
131 5 : va_start(ap, fmt);
132 5 : buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
133 5 : va_end(ap);
134 :
135 5 : buf = os_malloc(buflen);
136 5 : if (buf == NULL)
137 0 : return;
138 5 : va_start(ap, fmt);
139 5 : vsnprintf(buf, buflen, fmt, ap);
140 5 : va_end(ap);
141 :
142 5 : sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
143 :
144 5 : os_free(buf);
145 : }
146 :
147 :
148 672 : SM_STATE(EAP, DISABLED)
149 : {
150 672 : SM_ENTRY(EAP, DISABLED);
151 672 : sm->num_rounds = 0;
152 672 : }
153 :
154 :
155 441 : SM_STATE(EAP, INITIALIZE)
156 : {
157 441 : SM_ENTRY(EAP, INITIALIZE);
158 :
159 441 : if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
160 : /*
161 : * Need to allow internal Identity method to be used instead
162 : * of passthrough at the beginning of reauthentication.
163 : */
164 11 : eap_server_clear_identity(sm);
165 : }
166 :
167 441 : sm->currentId = -1;
168 441 : sm->eap_if.eapSuccess = FALSE;
169 441 : sm->eap_if.eapFail = FALSE;
170 441 : sm->eap_if.eapTimeout = FALSE;
171 441 : os_free(sm->eap_if.eapKeyData);
172 441 : sm->eap_if.eapKeyData = NULL;
173 441 : sm->eap_if.eapKeyDataLen = 0;
174 441 : sm->eap_if.eapKeyAvailable = FALSE;
175 441 : sm->eap_if.eapRestart = FALSE;
176 :
177 : /*
178 : * This is not defined in RFC 4137, but method state needs to be
179 : * reseted here so that it does not remain in success state when
180 : * re-authentication starts.
181 : */
182 441 : if (sm->m && sm->eap_method_priv) {
183 70 : sm->m->reset(sm, sm->eap_method_priv);
184 70 : sm->eap_method_priv = NULL;
185 : }
186 441 : sm->m = NULL;
187 441 : sm->user_eap_method_index = 0;
188 :
189 441 : if (sm->backend_auth) {
190 2 : sm->currentMethod = EAP_TYPE_NONE;
191 : /* parse rxResp, respId, respMethod */
192 2 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
193 2 : if (sm->rxResp) {
194 2 : sm->currentId = sm->respId;
195 : }
196 : }
197 441 : sm->num_rounds = 0;
198 441 : sm->method_pending = METHOD_PENDING_NONE;
199 :
200 2646 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
201 2646 : MACSTR, MAC2STR(sm->peer_addr));
202 441 : }
203 :
204 :
205 2 : SM_STATE(EAP, PICK_UP_METHOD)
206 : {
207 2 : SM_ENTRY(EAP, PICK_UP_METHOD);
208 :
209 2 : if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
210 2 : sm->currentMethod = sm->respMethod;
211 2 : if (sm->m && sm->eap_method_priv) {
212 0 : sm->m->reset(sm, sm->eap_method_priv);
213 0 : sm->eap_method_priv = NULL;
214 : }
215 2 : sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
216 : sm->currentMethod);
217 2 : if (sm->m && sm->m->initPickUp) {
218 2 : sm->eap_method_priv = sm->m->initPickUp(sm);
219 4 : if (sm->eap_method_priv == NULL) {
220 0 : wpa_printf(MSG_DEBUG, "EAP: Failed to "
221 : "initialize EAP method %d",
222 0 : sm->currentMethod);
223 0 : sm->m = NULL;
224 0 : sm->currentMethod = EAP_TYPE_NONE;
225 : }
226 : } else {
227 0 : sm->m = NULL;
228 0 : sm->currentMethod = EAP_TYPE_NONE;
229 : }
230 : }
231 :
232 2 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
233 2 : "method=%u", sm->currentMethod);
234 2 : }
235 :
236 :
237 1025 : SM_STATE(EAP, IDLE)
238 : {
239 1025 : SM_ENTRY(EAP, IDLE);
240 :
241 1025 : sm->eap_if.retransWhile = eap_sm_calculateTimeout(
242 : sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
243 : sm->methodTimeout);
244 1025 : }
245 :
246 :
247 0 : SM_STATE(EAP, RETRANSMIT)
248 : {
249 0 : SM_ENTRY(EAP, RETRANSMIT);
250 :
251 0 : sm->retransCount++;
252 0 : if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
253 0 : if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
254 0 : sm->eap_if.eapReq = TRUE;
255 : }
256 0 : }
257 :
258 :
259 1018 : SM_STATE(EAP, RECEIVED)
260 : {
261 1018 : SM_ENTRY(EAP, RECEIVED);
262 :
263 : /* parse rxResp, respId, respMethod */
264 1018 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
265 1018 : sm->num_rounds++;
266 1018 : }
267 :
268 :
269 1 : SM_STATE(EAP, DISCARD)
270 : {
271 1 : SM_ENTRY(EAP, DISCARD);
272 1 : sm->eap_if.eapResp = FALSE;
273 1 : sm->eap_if.eapNoReq = TRUE;
274 1 : }
275 :
276 :
277 1024 : SM_STATE(EAP, SEND_REQUEST)
278 : {
279 1024 : SM_ENTRY(EAP, SEND_REQUEST);
280 :
281 1024 : sm->retransCount = 0;
282 1024 : if (sm->eap_if.eapReqData) {
283 1024 : if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
284 : {
285 1024 : sm->eap_if.eapResp = FALSE;
286 1024 : sm->eap_if.eapReq = TRUE;
287 : } else {
288 0 : sm->eap_if.eapResp = FALSE;
289 0 : sm->eap_if.eapReq = FALSE;
290 : }
291 : } else {
292 0 : wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
293 0 : sm->eap_if.eapResp = FALSE;
294 0 : sm->eap_if.eapReq = FALSE;
295 0 : sm->eap_if.eapNoReq = TRUE;
296 : }
297 1024 : }
298 :
299 :
300 1008 : SM_STATE(EAP, INTEGRITY_CHECK)
301 : {
302 1008 : SM_ENTRY(EAP, INTEGRITY_CHECK);
303 :
304 1008 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
305 0 : sm->ignore = TRUE;
306 1008 : return;
307 : }
308 :
309 1008 : if (sm->m->check) {
310 1008 : sm->ignore = sm->m->check(sm, sm->eap_method_priv,
311 : sm->eap_if.eapRespData);
312 : }
313 : }
314 :
315 :
316 1024 : SM_STATE(EAP, METHOD_REQUEST)
317 : {
318 1024 : SM_ENTRY(EAP, METHOD_REQUEST);
319 :
320 1024 : if (sm->m == NULL) {
321 0 : wpa_printf(MSG_DEBUG, "EAP: method not initialized");
322 1024 : return;
323 : }
324 :
325 1024 : sm->currentId = eap_sm_nextId(sm, sm->currentId);
326 1024 : wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
327 : sm->currentId);
328 1024 : sm->lastId = sm->currentId;
329 1024 : wpabuf_free(sm->eap_if.eapReqData);
330 2048 : sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
331 1024 : sm->currentId);
332 1024 : if (sm->m->getTimeout)
333 422 : sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
334 : else
335 602 : sm->methodTimeout = 0;
336 : }
337 :
338 :
339 1040 : SM_STATE(EAP, METHOD_RESPONSE)
340 : {
341 1040 : SM_ENTRY(EAP, METHOD_RESPONSE);
342 :
343 1040 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
344 1040 : return;
345 :
346 1040 : sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
347 1040 : if (sm->m->isDone(sm, sm->eap_method_priv)) {
348 558 : eap_sm_Policy_update(sm, NULL, 0);
349 558 : os_free(sm->eap_if.eapKeyData);
350 558 : if (sm->m->getKey) {
351 39 : sm->eap_if.eapKeyData = sm->m->getKey(
352 : sm, sm->eap_method_priv,
353 : &sm->eap_if.eapKeyDataLen);
354 : } else {
355 519 : sm->eap_if.eapKeyData = NULL;
356 519 : sm->eap_if.eapKeyDataLen = 0;
357 : }
358 558 : sm->methodState = METHOD_END;
359 : } else {
360 482 : sm->methodState = METHOD_CONTINUE;
361 : }
362 : }
363 :
364 :
365 572 : SM_STATE(EAP, PROPOSE_METHOD)
366 : {
367 : int vendor;
368 : EapType type;
369 :
370 572 : SM_ENTRY(EAP, PROPOSE_METHOD);
371 :
372 : try_another_method:
373 572 : type = eap_sm_Policy_getNextMethod(sm, &vendor);
374 572 : if (vendor == EAP_VENDOR_IETF)
375 489 : sm->currentMethod = type;
376 : else
377 83 : sm->currentMethod = EAP_TYPE_EXPANDED;
378 572 : if (sm->m && sm->eap_method_priv) {
379 124 : sm->m->reset(sm, sm->eap_method_priv);
380 124 : sm->eap_method_priv = NULL;
381 : }
382 572 : sm->m = eap_server_get_eap_method(vendor, type);
383 572 : if (sm->m) {
384 572 : sm->eap_method_priv = sm->m->init(sm);
385 572 : if (sm->eap_method_priv == NULL) {
386 0 : wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
387 0 : "method %d", sm->currentMethod);
388 0 : sm->m = NULL;
389 0 : sm->currentMethod = EAP_TYPE_NONE;
390 0 : goto try_another_method;
391 : }
392 : }
393 572 : if (sm->m == NULL) {
394 0 : wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
395 0 : eap_log_msg(sm, "Could not find suitable EAP method");
396 0 : sm->decision = DECISION_FAILURE;
397 572 : return;
398 : }
399 705 : if (sm->currentMethod == EAP_TYPE_IDENTITY ||
400 133 : sm->currentMethod == EAP_TYPE_NOTIFICATION)
401 439 : sm->methodState = METHOD_CONTINUE;
402 : else
403 133 : sm->methodState = METHOD_PROPOSED;
404 :
405 572 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
406 572 : "vendor=%u method=%u", vendor, sm->currentMethod);
407 572 : eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
408 572 : vendor, sm->currentMethod);
409 : }
410 :
411 :
412 9 : SM_STATE(EAP, NAK)
413 : {
414 : const struct eap_hdr *nak;
415 9 : size_t len = 0;
416 : const u8 *pos;
417 9 : const u8 *nak_list = NULL;
418 :
419 9 : SM_ENTRY(EAP, NAK);
420 :
421 9 : if (sm->eap_method_priv) {
422 9 : sm->m->reset(sm, sm->eap_method_priv);
423 9 : sm->eap_method_priv = NULL;
424 : }
425 9 : sm->m = NULL;
426 :
427 9 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
428 9 : return;
429 :
430 9 : nak = wpabuf_head(sm->eap_if.eapRespData);
431 9 : if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
432 9 : len = be_to_host16(nak->length);
433 9 : if (len > wpabuf_len(sm->eap_if.eapRespData))
434 0 : len = wpabuf_len(sm->eap_if.eapRespData);
435 9 : pos = (const u8 *) (nak + 1);
436 9 : len -= sizeof(*nak);
437 9 : if (*pos == EAP_TYPE_NAK) {
438 9 : pos++;
439 9 : len--;
440 9 : nak_list = pos;
441 : }
442 : }
443 9 : eap_sm_Policy_update(sm, nak_list, len);
444 : }
445 :
446 :
447 1006 : SM_STATE(EAP, SELECT_ACTION)
448 : {
449 1006 : SM_ENTRY(EAP, SELECT_ACTION);
450 :
451 1006 : sm->decision = eap_sm_Policy_getDecision(sm);
452 1006 : }
453 :
454 :
455 0 : SM_STATE(EAP, TIMEOUT_FAILURE)
456 : {
457 0 : SM_ENTRY(EAP, TIMEOUT_FAILURE);
458 :
459 0 : sm->eap_if.eapTimeout = TRUE;
460 0 : }
461 :
462 :
463 87 : SM_STATE(EAP, FAILURE)
464 : {
465 87 : SM_ENTRY(EAP, FAILURE);
466 :
467 87 : wpabuf_free(sm->eap_if.eapReqData);
468 87 : sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
469 87 : wpabuf_free(sm->lastReqData);
470 87 : sm->lastReqData = NULL;
471 87 : sm->eap_if.eapFail = TRUE;
472 :
473 522 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
474 522 : MACSTR, MAC2STR(sm->peer_addr));
475 87 : }
476 :
477 :
478 33 : SM_STATE(EAP, SUCCESS)
479 : {
480 33 : SM_ENTRY(EAP, SUCCESS);
481 :
482 33 : wpabuf_free(sm->eap_if.eapReqData);
483 33 : sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
484 33 : wpabuf_free(sm->lastReqData);
485 33 : sm->lastReqData = NULL;
486 33 : if (sm->eap_if.eapKeyData)
487 33 : sm->eap_if.eapKeyAvailable = TRUE;
488 33 : sm->eap_if.eapSuccess = TRUE;
489 :
490 198 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
491 198 : MACSTR, MAC2STR(sm->peer_addr));
492 33 : }
493 :
494 :
495 314 : SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
496 : {
497 314 : SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
498 :
499 314 : wpabuf_free(sm->eap_if.aaaEapRespData);
500 314 : sm->eap_if.aaaEapRespData = NULL;
501 314 : }
502 :
503 :
504 1311 : SM_STATE(EAP, IDLE2)
505 : {
506 1311 : SM_ENTRY(EAP, IDLE2);
507 :
508 1311 : sm->eap_if.retransWhile = eap_sm_calculateTimeout(
509 : sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
510 : sm->methodTimeout);
511 1311 : }
512 :
513 :
514 0 : SM_STATE(EAP, RETRANSMIT2)
515 : {
516 0 : SM_ENTRY(EAP, RETRANSMIT2);
517 :
518 0 : sm->retransCount++;
519 0 : if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
520 0 : if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
521 0 : sm->eap_if.eapReq = TRUE;
522 : }
523 0 : }
524 :
525 :
526 1307 : SM_STATE(EAP, RECEIVED2)
527 : {
528 1307 : SM_ENTRY(EAP, RECEIVED2);
529 :
530 : /* parse rxResp, respId, respMethod */
531 1307 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
532 1307 : }
533 :
534 :
535 0 : SM_STATE(EAP, DISCARD2)
536 : {
537 0 : SM_ENTRY(EAP, DISCARD2);
538 0 : sm->eap_if.eapResp = FALSE;
539 0 : sm->eap_if.eapNoReq = TRUE;
540 0 : }
541 :
542 :
543 1311 : SM_STATE(EAP, SEND_REQUEST2)
544 : {
545 1311 : SM_ENTRY(EAP, SEND_REQUEST2);
546 :
547 1311 : sm->retransCount = 0;
548 1311 : if (sm->eap_if.eapReqData) {
549 1311 : if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
550 : {
551 1311 : sm->eap_if.eapResp = FALSE;
552 1311 : sm->eap_if.eapReq = TRUE;
553 : } else {
554 0 : sm->eap_if.eapResp = FALSE;
555 0 : sm->eap_if.eapReq = FALSE;
556 : }
557 : } else {
558 0 : wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
559 0 : sm->eap_if.eapResp = FALSE;
560 0 : sm->eap_if.eapReq = FALSE;
561 0 : sm->eap_if.eapNoReq = TRUE;
562 : }
563 1311 : }
564 :
565 :
566 1621 : SM_STATE(EAP, AAA_REQUEST)
567 : {
568 1621 : SM_ENTRY(EAP, AAA_REQUEST);
569 :
570 1621 : if (sm->eap_if.eapRespData == NULL) {
571 0 : wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
572 1621 : return;
573 : }
574 :
575 : /*
576 : * if (respMethod == IDENTITY)
577 : * aaaIdentity = eapRespData
578 : * This is already taken care of by the EAP-Identity method which
579 : * stores the identity into sm->identity.
580 : */
581 :
582 1621 : eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
583 : }
584 :
585 :
586 1311 : SM_STATE(EAP, AAA_RESPONSE)
587 : {
588 1311 : SM_ENTRY(EAP, AAA_RESPONSE);
589 :
590 1311 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
591 1311 : sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
592 1311 : sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
593 1311 : }
594 :
595 :
596 1621 : SM_STATE(EAP, AAA_IDLE)
597 : {
598 1621 : SM_ENTRY(EAP, AAA_IDLE);
599 :
600 1621 : sm->eap_if.aaaFail = FALSE;
601 1621 : sm->eap_if.aaaSuccess = FALSE;
602 1621 : sm->eap_if.aaaEapReq = FALSE;
603 1621 : sm->eap_if.aaaEapNoReq = FALSE;
604 1621 : sm->eap_if.aaaEapResp = TRUE;
605 1621 : }
606 :
607 :
608 0 : SM_STATE(EAP, TIMEOUT_FAILURE2)
609 : {
610 0 : SM_ENTRY(EAP, TIMEOUT_FAILURE2);
611 :
612 0 : sm->eap_if.eapTimeout = TRUE;
613 0 : }
614 :
615 :
616 46 : SM_STATE(EAP, FAILURE2)
617 : {
618 46 : SM_ENTRY(EAP, FAILURE2);
619 :
620 46 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
621 46 : sm->eap_if.eapFail = TRUE;
622 46 : }
623 :
624 :
625 255 : SM_STATE(EAP, SUCCESS2)
626 : {
627 255 : SM_ENTRY(EAP, SUCCESS2);
628 :
629 255 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
630 :
631 255 : sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
632 255 : if (sm->eap_if.aaaEapKeyAvailable) {
633 255 : EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
634 : } else {
635 0 : os_free(sm->eap_if.eapKeyData);
636 0 : sm->eap_if.eapKeyData = NULL;
637 0 : sm->eap_if.eapKeyDataLen = 0;
638 : }
639 :
640 255 : sm->eap_if.eapSuccess = TRUE;
641 :
642 : /*
643 : * Start reauthentication with identity request even though we know the
644 : * previously used identity. This is needed to get reauthentication
645 : * started properly.
646 : */
647 255 : sm->start_reauth = TRUE;
648 255 : }
649 :
650 :
651 28331 : SM_STEP(EAP)
652 : {
653 28331 : if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
654 441 : SM_ENTER_GLOBAL(EAP, INITIALIZE);
655 27890 : else if (!sm->eap_if.portEnabled)
656 672 : SM_ENTER_GLOBAL(EAP, DISABLED);
657 27218 : else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
658 0 : if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
659 0 : wpa_printf(MSG_DEBUG, "EAP: more than %d "
660 : "authentication rounds - abort",
661 : EAP_MAX_AUTH_ROUNDS);
662 0 : sm->num_rounds++;
663 0 : SM_ENTER_GLOBAL(EAP, FAILURE);
664 : }
665 27218 : } else switch (sm->EAP_state) {
666 : case EAP_INITIALIZE:
667 441 : if (sm->backend_auth) {
668 2 : if (!sm->rxResp)
669 0 : SM_ENTER(EAP, SELECT_ACTION);
670 4 : else if (sm->rxResp &&
671 4 : (sm->respMethod == EAP_TYPE_NAK ||
672 2 : (sm->respMethod == EAP_TYPE_EXPANDED &&
673 0 : sm->respVendor == EAP_VENDOR_IETF &&
674 0 : sm->respVendorMethod == EAP_TYPE_NAK)))
675 0 : SM_ENTER(EAP, NAK);
676 : else
677 2 : SM_ENTER(EAP, PICK_UP_METHOD);
678 : } else {
679 439 : SM_ENTER(EAP, SELECT_ACTION);
680 : }
681 441 : break;
682 : case EAP_PICK_UP_METHOD:
683 2 : if (sm->currentMethod == EAP_TYPE_NONE) {
684 0 : SM_ENTER(EAP, SELECT_ACTION);
685 : } else {
686 2 : SM_ENTER(EAP, METHOD_RESPONSE);
687 : }
688 2 : break;
689 : case EAP_DISABLED:
690 0 : if (sm->eap_if.portEnabled)
691 0 : SM_ENTER(EAP, INITIALIZE);
692 0 : break;
693 : case EAP_IDLE:
694 4255 : if (sm->eap_if.retransWhile == 0)
695 0 : SM_ENTER(EAP, RETRANSMIT);
696 4255 : else if (sm->eap_if.eapResp)
697 1018 : SM_ENTER(EAP, RECEIVED);
698 4255 : break;
699 : case EAP_RETRANSMIT:
700 0 : if (sm->retransCount > sm->MaxRetrans)
701 0 : SM_ENTER(EAP, TIMEOUT_FAILURE);
702 : else
703 0 : SM_ENTER(EAP, IDLE);
704 0 : break;
705 : case EAP_RECEIVED:
706 2035 : if (sm->rxResp && (sm->respId == sm->currentId) &&
707 2025 : (sm->respMethod == EAP_TYPE_NAK ||
708 1428 : (sm->respMethod == EAP_TYPE_EXPANDED &&
709 420 : sm->respVendor == EAP_VENDOR_IETF &&
710 0 : sm->respVendorMethod == EAP_TYPE_NAK))
711 9 : && (sm->methodState == METHOD_PROPOSED))
712 9 : SM_ENTER(EAP, NAK);
713 2017 : else if (sm->rxResp && (sm->respId == sm->currentId) &&
714 1008 : ((sm->respMethod == sm->currentMethod) ||
715 0 : (sm->respMethod == EAP_TYPE_EXPANDED &&
716 0 : sm->respVendor == EAP_VENDOR_IETF &&
717 0 : sm->respVendorMethod == sm->currentMethod)))
718 1008 : SM_ENTER(EAP, INTEGRITY_CHECK);
719 : else {
720 2 : wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
721 : "rxResp=%d respId=%d currentId=%d "
722 : "respMethod=%d currentMethod=%d",
723 1 : sm->rxResp, sm->respId, sm->currentId,
724 1 : sm->respMethod, sm->currentMethod);
725 1 : eap_log_msg(sm, "Discard received EAP message");
726 1 : SM_ENTER(EAP, DISCARD);
727 : }
728 1018 : break;
729 : case EAP_DISCARD:
730 1 : SM_ENTER(EAP, IDLE);
731 1 : break;
732 : case EAP_SEND_REQUEST:
733 1024 : SM_ENTER(EAP, IDLE);
734 1024 : break;
735 : case EAP_INTEGRITY_CHECK:
736 1008 : if (sm->ignore)
737 0 : SM_ENTER(EAP, DISCARD);
738 : else
739 1008 : SM_ENTER(EAP, METHOD_RESPONSE);
740 1008 : break;
741 : case EAP_METHOD_REQUEST:
742 1024 : if (sm->m == NULL) {
743 : /*
744 : * This transition is not mentioned in RFC 4137, but it
745 : * is needed to handle cleanly a case where EAP method
746 : * initialization fails.
747 : */
748 0 : SM_ENTER(EAP, FAILURE);
749 0 : break;
750 : }
751 1024 : SM_ENTER(EAP, SEND_REQUEST);
752 1024 : break;
753 : case EAP_METHOD_RESPONSE:
754 : /*
755 : * Note: Mechanism to allow EAP methods to wait while going
756 : * through pending processing is an extension to RFC 4137
757 : * which only defines the transits to SELECT_ACTION and
758 : * METHOD_REQUEST from this METHOD_RESPONSE state.
759 : */
760 1105 : if (sm->methodState == METHOD_END)
761 558 : SM_ENTER(EAP, SELECT_ACTION);
762 547 : else if (sm->method_pending == METHOD_PENDING_WAIT) {
763 65 : wpa_printf(MSG_DEBUG, "EAP: Method has pending "
764 : "processing - wait before proceeding to "
765 : "METHOD_REQUEST state");
766 482 : } else if (sm->method_pending == METHOD_PENDING_CONT) {
767 30 : wpa_printf(MSG_DEBUG, "EAP: Method has completed "
768 : "pending processing - reprocess pending "
769 : "EAP message");
770 30 : sm->method_pending = METHOD_PENDING_NONE;
771 30 : SM_ENTER(EAP, METHOD_RESPONSE);
772 : } else
773 452 : SM_ENTER(EAP, METHOD_REQUEST);
774 1105 : break;
775 : case EAP_PROPOSE_METHOD:
776 : /*
777 : * Note: Mechanism to allow EAP methods to wait while going
778 : * through pending processing is an extension to RFC 4137
779 : * which only defines the transit to METHOD_REQUEST from this
780 : * PROPOSE_METHOD state.
781 : */
782 572 : if (sm->method_pending == METHOD_PENDING_WAIT) {
783 0 : wpa_printf(MSG_DEBUG, "EAP: Method has pending "
784 : "processing - wait before proceeding to "
785 : "METHOD_REQUEST state");
786 0 : if (sm->user_eap_method_index > 0)
787 0 : sm->user_eap_method_index--;
788 572 : } else if (sm->method_pending == METHOD_PENDING_CONT) {
789 0 : wpa_printf(MSG_DEBUG, "EAP: Method has completed "
790 : "pending processing - reprocess pending "
791 : "EAP message");
792 0 : sm->method_pending = METHOD_PENDING_NONE;
793 0 : SM_ENTER(EAP, PROPOSE_METHOD);
794 : } else
795 572 : SM_ENTER(EAP, METHOD_REQUEST);
796 572 : break;
797 : case EAP_NAK:
798 9 : SM_ENTER(EAP, SELECT_ACTION);
799 9 : break;
800 : case EAP_SELECT_ACTION:
801 1006 : if (sm->decision == DECISION_FAILURE)
802 87 : SM_ENTER(EAP, FAILURE);
803 919 : else if (sm->decision == DECISION_SUCCESS)
804 33 : SM_ENTER(EAP, SUCCESS);
805 886 : else if (sm->decision == DECISION_PASSTHROUGH)
806 314 : SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
807 : else
808 572 : SM_ENTER(EAP, PROPOSE_METHOD);
809 1006 : break;
810 : case EAP_TIMEOUT_FAILURE:
811 0 : break;
812 : case EAP_FAILURE:
813 87 : break;
814 : case EAP_SUCCESS:
815 141 : break;
816 :
817 : case EAP_INITIALIZE_PASSTHROUGH:
818 314 : if (sm->currentId == -1)
819 0 : SM_ENTER(EAP, AAA_IDLE);
820 : else
821 314 : SM_ENTER(EAP, AAA_REQUEST);
822 314 : break;
823 : case EAP_IDLE2:
824 3932 : if (sm->eap_if.eapResp)
825 1307 : SM_ENTER(EAP, RECEIVED2);
826 2625 : else if (sm->eap_if.retransWhile == 0)
827 0 : SM_ENTER(EAP, RETRANSMIT2);
828 3932 : break;
829 : case EAP_RETRANSMIT2:
830 0 : if (sm->retransCount > sm->MaxRetrans)
831 0 : SM_ENTER(EAP, TIMEOUT_FAILURE2);
832 : else
833 0 : SM_ENTER(EAP, IDLE2);
834 0 : break;
835 : case EAP_RECEIVED2:
836 1307 : if (sm->rxResp && (sm->respId == sm->currentId))
837 1307 : SM_ENTER(EAP, AAA_REQUEST);
838 : else
839 0 : SM_ENTER(EAP, DISCARD2);
840 1307 : break;
841 : case EAP_DISCARD2:
842 0 : SM_ENTER(EAP, IDLE2);
843 0 : break;
844 : case EAP_SEND_REQUEST2:
845 1311 : SM_ENTER(EAP, IDLE2);
846 1311 : break;
847 : case EAP_AAA_REQUEST:
848 1621 : SM_ENTER(EAP, AAA_IDLE);
849 1621 : break;
850 : case EAP_AAA_RESPONSE:
851 1311 : SM_ENTER(EAP, SEND_REQUEST2);
852 1311 : break;
853 : case EAP_AAA_IDLE:
854 4872 : if (sm->eap_if.aaaFail)
855 46 : SM_ENTER(EAP, FAILURE2);
856 4826 : else if (sm->eap_if.aaaSuccess)
857 255 : SM_ENTER(EAP, SUCCESS2);
858 4571 : else if (sm->eap_if.aaaEapReq)
859 1311 : SM_ENTER(EAP, AAA_RESPONSE);
860 3260 : else if (sm->eap_if.aaaTimeout)
861 0 : SM_ENTER(EAP, TIMEOUT_FAILURE2);
862 4872 : break;
863 : case EAP_TIMEOUT_FAILURE2:
864 0 : break;
865 : case EAP_FAILURE2:
866 46 : break;
867 : case EAP_SUCCESS2:
868 811 : break;
869 : }
870 28331 : }
871 :
872 :
873 2336 : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
874 : int eapSRTT, int eapRTTVAR,
875 : int methodTimeout)
876 : {
877 : int rto, i;
878 :
879 2336 : if (methodTimeout) {
880 : /*
881 : * EAP method (either internal or through AAA server, provided
882 : * timeout hint. Use that as-is as a timeout for retransmitting
883 : * the EAP request if no response is received.
884 : */
885 422 : wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
886 : "(from EAP method hint)", methodTimeout);
887 422 : return methodTimeout;
888 : }
889 :
890 : /*
891 : * RFC 3748 recommends algorithms described in RFC 2988 for estimation
892 : * of the retransmission timeout. This should be implemented once
893 : * round-trip time measurements are available. For nowm a simple
894 : * backoff mechanism is used instead if there are no EAP method
895 : * specific hints.
896 : *
897 : * SRTT = smoothed round-trip time
898 : * RTTVAR = round-trip time variation
899 : * RTO = retransmission timeout
900 : */
901 :
902 : /*
903 : * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
904 : * initial retransmission and then double the RTO to provide back off
905 : * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
906 : * modified RTOmax.
907 : */
908 1914 : rto = 3;
909 1914 : for (i = 0; i < retransCount; i++) {
910 0 : rto *= 2;
911 0 : if (rto >= 20) {
912 0 : rto = 20;
913 0 : break;
914 : }
915 : }
916 :
917 1914 : wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
918 : "(from dynamic back off; retransCount=%d)",
919 : rto, retransCount);
920 :
921 1914 : return rto;
922 : }
923 :
924 :
925 2327 : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
926 : {
927 : const struct eap_hdr *hdr;
928 : size_t plen;
929 :
930 : /* parse rxResp, respId, respMethod */
931 2327 : sm->rxResp = FALSE;
932 2327 : sm->respId = -1;
933 2327 : sm->respMethod = EAP_TYPE_NONE;
934 2327 : sm->respVendor = EAP_VENDOR_IETF;
935 2327 : sm->respVendorMethod = EAP_TYPE_NONE;
936 :
937 2327 : if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
938 0 : wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
939 : "len=%lu", resp,
940 : resp ? (unsigned long) wpabuf_len(resp) : 0);
941 0 : return;
942 : }
943 :
944 2327 : hdr = wpabuf_head(resp);
945 2327 : plen = be_to_host16(hdr->length);
946 2327 : if (plen > wpabuf_len(resp)) {
947 0 : wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
948 : "(len=%lu plen=%lu)",
949 : (unsigned long) wpabuf_len(resp),
950 : (unsigned long) plen);
951 0 : return;
952 : }
953 :
954 2327 : sm->respId = hdr->identifier;
955 :
956 2327 : if (hdr->code == EAP_CODE_RESPONSE)
957 2327 : sm->rxResp = TRUE;
958 :
959 2327 : if (plen > sizeof(*hdr)) {
960 2327 : u8 *pos = (u8 *) (hdr + 1);
961 2327 : sm->respMethod = *pos++;
962 2327 : if (sm->respMethod == EAP_TYPE_EXPANDED) {
963 441 : if (plen < sizeof(*hdr) + 8) {
964 0 : wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
965 : "expanded EAP-Packet (plen=%lu)",
966 : (unsigned long) plen);
967 0 : return;
968 : }
969 441 : sm->respVendor = WPA_GET_BE24(pos);
970 441 : pos += 3;
971 441 : sm->respVendorMethod = WPA_GET_BE32(pos);
972 : }
973 : }
974 :
975 6981 : wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
976 : "respMethod=%u respVendor=%u respVendorMethod=%u",
977 4654 : sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
978 : sm->respVendorMethod);
979 : }
980 :
981 :
982 1311 : static int eap_sm_getId(const struct wpabuf *data)
983 : {
984 : const struct eap_hdr *hdr;
985 :
986 1311 : if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
987 0 : return -1;
988 :
989 1311 : hdr = wpabuf_head(data);
990 1311 : wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
991 1311 : return hdr->identifier;
992 : }
993 :
994 :
995 33 : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
996 : {
997 : struct wpabuf *msg;
998 : struct eap_hdr *resp;
999 33 : wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
1000 :
1001 33 : msg = wpabuf_alloc(sizeof(*resp));
1002 33 : if (msg == NULL)
1003 0 : return NULL;
1004 33 : resp = wpabuf_put(msg, sizeof(*resp));
1005 33 : resp->code = EAP_CODE_SUCCESS;
1006 33 : resp->identifier = id;
1007 33 : resp->length = host_to_be16(sizeof(*resp));
1008 :
1009 33 : return msg;
1010 : }
1011 :
1012 :
1013 87 : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
1014 : {
1015 : struct wpabuf *msg;
1016 : struct eap_hdr *resp;
1017 87 : wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
1018 :
1019 87 : msg = wpabuf_alloc(sizeof(*resp));
1020 87 : if (msg == NULL)
1021 0 : return NULL;
1022 87 : resp = wpabuf_put(msg, sizeof(*resp));
1023 87 : resp->code = EAP_CODE_FAILURE;
1024 87 : resp->identifier = id;
1025 87 : resp->length = host_to_be16(sizeof(*resp));
1026 :
1027 87 : return msg;
1028 : }
1029 :
1030 :
1031 1024 : static int eap_sm_nextId(struct eap_sm *sm, int id)
1032 : {
1033 1024 : if (id < 0) {
1034 : /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
1035 : * random number */
1036 439 : id = rand() & 0xff;
1037 439 : if (id != sm->lastId)
1038 438 : return id;
1039 : }
1040 586 : return (id + 1) & 0xff;
1041 : }
1042 :
1043 :
1044 : /**
1045 : * eap_sm_process_nak - Process EAP-Response/Nak
1046 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1047 : * @nak_list: Nak list (allowed methods) from the supplicant
1048 : * @len: Length of nak_list in bytes
1049 : *
1050 : * This function is called when EAP-Response/Nak is received from the
1051 : * supplicant. This can happen for both phase 1 and phase 2 authentications.
1052 : */
1053 10 : void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
1054 : {
1055 : int i;
1056 : size_t j;
1057 :
1058 10 : if (sm->user == NULL)
1059 10 : return;
1060 :
1061 10 : wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
1062 : "index %d)", sm->user_eap_method_index);
1063 :
1064 10 : wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
1065 10 : (u8 *) sm->user->methods,
1066 : EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
1067 10 : wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
1068 : nak_list, len);
1069 :
1070 10 : i = sm->user_eap_method_index;
1071 142 : while (i < EAP_MAX_METHODS &&
1072 132 : (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
1073 66 : sm->user->methods[i].method != EAP_TYPE_NONE)) {
1074 56 : if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
1075 0 : goto not_found;
1076 102 : for (j = 0; j < len; j++) {
1077 56 : if (nak_list[j] == sm->user->methods[i].method) {
1078 10 : break;
1079 : }
1080 : }
1081 :
1082 56 : if (j < len) {
1083 : /* found */
1084 10 : i++;
1085 10 : continue;
1086 : }
1087 :
1088 : not_found:
1089 : /* not found - remove from the list */
1090 46 : if (i + 1 < EAP_MAX_METHODS) {
1091 46 : os_memmove(&sm->user->methods[i],
1092 : &sm->user->methods[i + 1],
1093 : (EAP_MAX_METHODS - i - 1) *
1094 : sizeof(sm->user->methods[0]));
1095 : }
1096 46 : sm->user->methods[EAP_MAX_METHODS - 1].vendor =
1097 : EAP_VENDOR_IETF;
1098 46 : sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
1099 : }
1100 :
1101 10 : wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
1102 10 : (u8 *) sm->user->methods, EAP_MAX_METHODS *
1103 : sizeof(sm->user->methods[0]));
1104 : }
1105 :
1106 :
1107 567 : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
1108 : size_t len)
1109 : {
1110 567 : if (nak_list == NULL || sm == NULL || sm->user == NULL)
1111 558 : return;
1112 :
1113 9 : if (sm->user->phase2) {
1114 0 : wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
1115 : " info was selected - reject");
1116 0 : sm->decision = DECISION_FAILURE;
1117 0 : return;
1118 : }
1119 :
1120 9 : eap_sm_process_nak(sm, nak_list, len);
1121 : }
1122 :
1123 :
1124 572 : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
1125 : {
1126 : EapType next;
1127 572 : int idx = sm->user_eap_method_index;
1128 :
1129 : /* In theory, there should be no problems with starting
1130 : * re-authentication with something else than EAP-Request/Identity and
1131 : * this does indeed work with wpa_supplicant. However, at least Funk
1132 : * Supplicant seemed to ignore re-auth if it skipped
1133 : * EAP-Request/Identity.
1134 : * Re-auth sets currentId == -1, so that can be used here to select
1135 : * whether Identity needs to be requested again. */
1136 572 : if (sm->identity == NULL || sm->currentId == -1) {
1137 439 : *vendor = EAP_VENDOR_IETF;
1138 439 : next = EAP_TYPE_IDENTITY;
1139 439 : sm->update_user = TRUE;
1140 266 : } else if (sm->user && idx < EAP_MAX_METHODS &&
1141 183 : (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
1142 50 : sm->user->methods[idx].method != EAP_TYPE_NONE)) {
1143 133 : *vendor = sm->user->methods[idx].vendor;
1144 133 : next = sm->user->methods[idx].method;
1145 133 : sm->user_eap_method_index++;
1146 : } else {
1147 0 : *vendor = EAP_VENDOR_IETF;
1148 0 : next = EAP_TYPE_NONE;
1149 : }
1150 572 : wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
1151 : *vendor, next);
1152 572 : return next;
1153 : }
1154 :
1155 :
1156 1006 : static int eap_sm_Policy_getDecision(struct eap_sm *sm)
1157 : {
1158 1006 : if (!sm->eap_server && sm->identity && !sm->start_reauth) {
1159 314 : wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
1160 314 : return DECISION_PASSTHROUGH;
1161 : }
1162 :
1163 812 : if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
1164 120 : sm->m->isSuccess(sm, sm->eap_method_priv)) {
1165 33 : wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
1166 : "SUCCESS");
1167 33 : sm->update_user = TRUE;
1168 33 : return DECISION_SUCCESS;
1169 : }
1170 :
1171 870 : if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
1172 211 : !sm->m->isSuccess(sm, sm->eap_method_priv)) {
1173 87 : wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
1174 : "FAILURE");
1175 87 : sm->update_user = TRUE;
1176 87 : return DECISION_FAILURE;
1177 : }
1178 :
1179 696 : if ((sm->user == NULL || sm->update_user) && sm->identity &&
1180 124 : !sm->start_reauth) {
1181 : /*
1182 : * Allow Identity method to be started once to allow identity
1183 : * selection hint to be sent from the authentication server,
1184 : * but prevent a loop of Identity requests by only allowing
1185 : * this to happen once.
1186 : */
1187 124 : int id_req = 0;
1188 128 : if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
1189 8 : sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
1190 4 : sm->user->methods[0].method == EAP_TYPE_IDENTITY)
1191 0 : id_req = 1;
1192 124 : if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
1193 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
1194 : "found from database -> FAILURE");
1195 0 : return DECISION_FAILURE;
1196 : }
1197 124 : if (id_req && sm->user &&
1198 0 : sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
1199 0 : sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
1200 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
1201 : "identity request loop -> FAILURE");
1202 0 : sm->update_user = TRUE;
1203 0 : return DECISION_FAILURE;
1204 : }
1205 124 : sm->update_user = FALSE;
1206 : }
1207 572 : sm->start_reauth = FALSE;
1208 :
1209 709 : if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
1210 137 : (sm->user->methods[sm->user_eap_method_index].vendor !=
1211 54 : EAP_VENDOR_IETF ||
1212 54 : sm->user->methods[sm->user_eap_method_index].method !=
1213 : EAP_TYPE_NONE)) {
1214 137 : wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
1215 : "available -> CONTINUE");
1216 137 : return DECISION_CONTINUE;
1217 : }
1218 :
1219 435 : if (sm->identity == NULL || sm->currentId == -1) {
1220 435 : wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
1221 : "yet -> CONTINUE");
1222 435 : return DECISION_CONTINUE;
1223 : }
1224 :
1225 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
1226 : "FAILURE");
1227 0 : return DECISION_FAILURE;
1228 : }
1229 :
1230 :
1231 2 : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
1232 : {
1233 2 : return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
1234 : }
1235 :
1236 :
1237 : /**
1238 : * eap_server_sm_step - Step EAP server state machine
1239 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1240 : * Returns: 1 if EAP state was changed or 0 if not
1241 : *
1242 : * This function advances EAP state machine to a new state to match with the
1243 : * current variables. This should be called whenever variables used by the EAP
1244 : * state machine have changed.
1245 : */
1246 10809 : int eap_server_sm_step(struct eap_sm *sm)
1247 : {
1248 10809 : int res = 0;
1249 : do {
1250 28331 : sm->changed = FALSE;
1251 28331 : SM_STEP_RUN(EAP);
1252 28331 : if (sm->changed)
1253 17522 : res = 1;
1254 28331 : } while (sm->changed);
1255 10809 : return res;
1256 : }
1257 :
1258 :
1259 526 : static void eap_user_free(struct eap_user *user)
1260 : {
1261 526 : if (user == NULL)
1262 910 : return;
1263 142 : os_free(user->password);
1264 142 : user->password = NULL;
1265 142 : os_free(user);
1266 : }
1267 :
1268 :
1269 : /**
1270 : * eap_server_sm_init - Allocate and initialize EAP server state machine
1271 : * @eapol_ctx: Context data to be used with eapol_cb calls
1272 : * @eapol_cb: Pointer to EAPOL callback functions
1273 : * @conf: EAP configuration
1274 : * Returns: Pointer to the allocated EAP state machine or %NULL on failure
1275 : *
1276 : * This function allocates and initializes an EAP state machine.
1277 : */
1278 383 : struct eap_sm * eap_server_sm_init(void *eapol_ctx,
1279 : struct eapol_callbacks *eapol_cb,
1280 : struct eap_config *conf)
1281 : {
1282 : struct eap_sm *sm;
1283 :
1284 383 : sm = os_zalloc(sizeof(*sm));
1285 383 : if (sm == NULL)
1286 0 : return NULL;
1287 383 : sm->eapol_ctx = eapol_ctx;
1288 383 : sm->eapol_cb = eapol_cb;
1289 383 : sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
1290 383 : sm->ssl_ctx = conf->ssl_ctx;
1291 383 : sm->msg_ctx = conf->msg_ctx;
1292 383 : sm->eap_sim_db_priv = conf->eap_sim_db_priv;
1293 383 : sm->backend_auth = conf->backend_auth;
1294 383 : sm->eap_server = conf->eap_server;
1295 383 : if (conf->pac_opaque_encr_key) {
1296 1 : sm->pac_opaque_encr_key = os_malloc(16);
1297 1 : if (sm->pac_opaque_encr_key) {
1298 1 : os_memcpy(sm->pac_opaque_encr_key,
1299 : conf->pac_opaque_encr_key, 16);
1300 : }
1301 : }
1302 383 : if (conf->eap_fast_a_id) {
1303 1 : sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
1304 1 : if (sm->eap_fast_a_id) {
1305 1 : os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
1306 : conf->eap_fast_a_id_len);
1307 1 : sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
1308 : }
1309 : }
1310 383 : if (conf->eap_fast_a_id_info)
1311 1 : sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
1312 383 : sm->eap_fast_prov = conf->eap_fast_prov;
1313 383 : sm->pac_key_lifetime = conf->pac_key_lifetime;
1314 383 : sm->pac_key_refresh_time = conf->pac_key_refresh_time;
1315 383 : sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
1316 383 : sm->tnc = conf->tnc;
1317 383 : sm->wps = conf->wps;
1318 383 : if (conf->assoc_wps_ie)
1319 83 : sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
1320 383 : if (conf->assoc_p2p_ie)
1321 0 : sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
1322 383 : if (conf->peer_addr)
1323 381 : os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
1324 383 : sm->fragment_size = conf->fragment_size;
1325 383 : sm->pwd_group = conf->pwd_group;
1326 383 : sm->pbc_in_m1 = conf->pbc_in_m1;
1327 383 : sm->server_id = conf->server_id;
1328 383 : sm->server_id_len = conf->server_id_len;
1329 :
1330 : #ifdef CONFIG_TESTING_OPTIONS
1331 383 : sm->tls_test_flags = conf->tls_test_flags;
1332 : #endif /* CONFIG_TESTING_OPTIONS */
1333 :
1334 383 : wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
1335 :
1336 383 : return sm;
1337 : }
1338 :
1339 :
1340 : /**
1341 : * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
1342 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1343 : *
1344 : * This function deinitializes EAP state machine and frees all allocated
1345 : * resources.
1346 : */
1347 383 : void eap_server_sm_deinit(struct eap_sm *sm)
1348 : {
1349 383 : if (sm == NULL)
1350 383 : return;
1351 383 : wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
1352 383 : if (sm->m && sm->eap_method_priv)
1353 371 : sm->m->reset(sm, sm->eap_method_priv);
1354 383 : wpabuf_free(sm->eap_if.eapReqData);
1355 383 : os_free(sm->eap_if.eapKeyData);
1356 383 : wpabuf_free(sm->lastReqData);
1357 383 : wpabuf_free(sm->eap_if.eapRespData);
1358 383 : os_free(sm->identity);
1359 383 : os_free(sm->pac_opaque_encr_key);
1360 383 : os_free(sm->eap_fast_a_id);
1361 383 : os_free(sm->eap_fast_a_id_info);
1362 383 : wpabuf_free(sm->eap_if.aaaEapReqData);
1363 383 : wpabuf_free(sm->eap_if.aaaEapRespData);
1364 383 : os_free(sm->eap_if.aaaEapKeyData);
1365 383 : eap_user_free(sm->user);
1366 383 : wpabuf_free(sm->assoc_wps_ie);
1367 383 : wpabuf_free(sm->assoc_p2p_ie);
1368 383 : os_free(sm);
1369 : }
1370 :
1371 :
1372 : /**
1373 : * eap_sm_notify_cached - Notify EAP state machine of cached PMK
1374 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1375 : *
1376 : * This function is called when PMKSA caching is used to skip EAP
1377 : * authentication.
1378 : */
1379 13 : void eap_sm_notify_cached(struct eap_sm *sm)
1380 : {
1381 13 : if (sm == NULL)
1382 13 : return;
1383 :
1384 13 : sm->EAP_state = EAP_SUCCESS;
1385 : }
1386 :
1387 :
1388 : /**
1389 : * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
1390 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1391 : *
1392 : * This function is called when data for a pending EAP-Request is received.
1393 : */
1394 30 : void eap_sm_pending_cb(struct eap_sm *sm)
1395 : {
1396 30 : if (sm == NULL)
1397 30 : return;
1398 30 : wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
1399 30 : if (sm->method_pending == METHOD_PENDING_WAIT)
1400 30 : sm->method_pending = METHOD_PENDING_CONT;
1401 : }
1402 :
1403 :
1404 : /**
1405 : * eap_sm_method_pending - Query whether EAP method is waiting for pending data
1406 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1407 : * Returns: 1 if method is waiting for pending data or 0 if not
1408 : */
1409 0 : int eap_sm_method_pending(struct eap_sm *sm)
1410 : {
1411 0 : if (sm == NULL)
1412 0 : return 0;
1413 0 : return sm->method_pending == METHOD_PENDING_WAIT;
1414 : }
1415 :
1416 :
1417 : /**
1418 : * eap_get_identity - Get the user identity (from EAP-Response/Identity)
1419 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1420 : * @len: Buffer for returning identity length
1421 : * Returns: Pointer to the user identity or %NULL if not available
1422 : */
1423 314 : const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
1424 : {
1425 314 : *len = sm->identity_len;
1426 314 : return sm->identity;
1427 : }
1428 :
1429 :
1430 : /**
1431 : * eap_get_interface - Get pointer to EAP-EAPOL interface data
1432 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1433 : * Returns: Pointer to the EAP-EAPOL interface data
1434 : */
1435 383 : struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
1436 : {
1437 383 : return &sm->eap_if;
1438 : }
1439 :
1440 :
1441 : /**
1442 : * eap_server_clear_identity - Clear EAP identity information
1443 : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1444 : *
1445 : * This function can be used to clear the EAP identity information in the EAP
1446 : * server context. This allows the EAP/Identity method to be used again after
1447 : * EAPOL-Start or EAPOL-Logoff.
1448 : */
1449 85 : void eap_server_clear_identity(struct eap_sm *sm)
1450 : {
1451 85 : os_free(sm->identity);
1452 85 : sm->identity = NULL;
1453 85 : }
|