]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine | |
3 | * Copyright (c) 2002-2008, Jouni Malinen <j@w1.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 | ||
15 | #include "includes.h" | |
16 | ||
17 | #include "hostapd.h" | |
18 | #include "ieee802_1x.h" | |
19 | #include "eapol_sm.h" | |
20 | #include "eloop.h" | |
21 | #include "wpa.h" | |
22 | #include "preauth.h" | |
23 | #include "sta_info.h" | |
24 | #include "eap_server/eap.h" | |
25 | #include "state_machine.h" | |
26 | #include "eap_common/eap_common.h" | |
27 | ||
28 | #define STATE_MACHINE_DATA struct eapol_state_machine | |
29 | #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" | |
30 | #define STATE_MACHINE_ADDR sm->addr | |
31 | ||
32 | static struct eapol_callbacks eapol_cb; | |
33 | ||
34 | /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ | |
35 | ||
36 | #define setPortAuthorized() \ | |
37 | sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 1) | |
38 | #define setPortUnauthorized() \ | |
39 | sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 0) | |
40 | ||
41 | /* procedures */ | |
42 | #define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) | |
43 | #define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) | |
44 | #define txReq() eapol_auth_tx_req(sm) | |
45 | #define abortAuth() sm->eapol->cb.abort_auth(sm->hapd, sm->sta) | |
46 | #define txKey() sm->eapol->cb.tx_key(sm->hapd, sm->sta) | |
47 | #define processKey() do { } while (0) | |
48 | ||
49 | ||
50 | static void eapol_sm_step_run(struct eapol_state_machine *sm); | |
51 | static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); | |
52 | ||
53 | ||
54 | static void eapol_auth_logger(struct eapol_authenticator *eapol, | |
55 | const u8 *addr, logger_level level, | |
56 | const char *txt) | |
57 | { | |
58 | if (eapol->cb.logger == NULL) | |
59 | return; | |
60 | eapol->cb.logger(eapol->conf.hapd, addr, level, txt); | |
61 | } | |
62 | ||
63 | ||
64 | static void eapol_auth_vlogger(struct eapol_authenticator *eapol, | |
65 | const u8 *addr, logger_level level, | |
66 | const char *fmt, ...) | |
67 | { | |
68 | char *format; | |
69 | int maxlen; | |
70 | va_list ap; | |
71 | ||
72 | if (eapol->cb.logger == NULL) | |
73 | return; | |
74 | ||
75 | maxlen = os_strlen(fmt) + 100; | |
76 | format = os_malloc(maxlen); | |
77 | if (!format) | |
78 | return; | |
79 | ||
80 | va_start(ap, fmt); | |
81 | vsnprintf(format, maxlen, fmt, ap); | |
82 | va_end(ap); | |
83 | ||
84 | eapol_auth_logger(eapol, addr, level, format); | |
85 | ||
86 | os_free(format); | |
87 | } | |
88 | ||
89 | ||
90 | static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, | |
91 | int success) | |
92 | { | |
93 | struct eap_hdr eap; | |
94 | ||
95 | os_memset(&eap, 0, sizeof(eap)); | |
96 | ||
97 | eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; | |
98 | eap.identifier = ++sm->last_eap_id; | |
99 | eap.length = host_to_be16(sizeof(eap)); | |
100 | ||
101 | eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, | |
102 | "Sending canned EAP packet %s (identifier %d)", | |
103 | success ? "SUCCESS" : "FAILURE", eap.identifier); | |
104 | sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, | |
105 | (u8 *) &eap, sizeof(eap)); | |
106 | sm->dot1xAuthEapolFramesTx++; | |
107 | } | |
108 | ||
109 | ||
110 | static void eapol_auth_tx_req(struct eapol_state_machine *sm) | |
111 | { | |
112 | if (sm->eap_if->eapReqData == NULL || | |
113 | wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { | |
114 | eapol_auth_logger(sm->eapol, sm->addr, | |
115 | EAPOL_LOGGER_DEBUG, | |
116 | "TxReq called, but there is no EAP request " | |
117 | "from authentication server"); | |
118 | return; | |
119 | } | |
120 | ||
121 | if (sm->flags & EAPOL_SM_WAIT_START) { | |
122 | wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR | |
123 | " while waiting for EAPOL-Start", | |
124 | MAC2STR(sm->addr)); | |
125 | return; | |
126 | } | |
127 | ||
128 | sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); | |
129 | eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, | |
130 | "Sending EAP Packet (identifier %d)", | |
131 | sm->last_eap_id); | |
132 | sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, | |
133 | wpabuf_head(sm->eap_if->eapReqData), | |
134 | wpabuf_len(sm->eap_if->eapReqData)); | |
135 | sm->dot1xAuthEapolFramesTx++; | |
136 | if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) | |
137 | sm->dot1xAuthEapolReqIdFramesTx++; | |
138 | else | |
139 | sm->dot1xAuthEapolReqFramesTx++; | |
140 | } | |
141 | ||
142 | ||
143 | /* Port Timers state machine - implemented as a function that will be called | |
144 | * once a second as a registered event loop timeout */ | |
145 | ||
146 | static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) | |
147 | { | |
148 | struct eapol_state_machine *state = timeout_ctx; | |
149 | ||
150 | if (state->aWhile > 0) { | |
151 | state->aWhile--; | |
152 | if (state->aWhile == 0) { | |
153 | wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR | |
154 | " - aWhile --> 0", | |
155 | MAC2STR(state->addr)); | |
156 | } | |
157 | } | |
158 | ||
159 | if (state->quietWhile > 0) { | |
160 | state->quietWhile--; | |
161 | if (state->quietWhile == 0) { | |
162 | wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR | |
163 | " - quietWhile --> 0", | |
164 | MAC2STR(state->addr)); | |
165 | } | |
166 | } | |
167 | ||
168 | if (state->reAuthWhen > 0) { | |
169 | state->reAuthWhen--; | |
170 | if (state->reAuthWhen == 0) { | |
171 | wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR | |
172 | " - reAuthWhen --> 0", | |
173 | MAC2STR(state->addr)); | |
174 | } | |
175 | } | |
176 | ||
8e09c6d2 JM |
177 | if (state->eap_if->retransWhile > 0) { |
178 | state->eap_if->retransWhile--; | |
179 | if (state->eap_if->retransWhile == 0) { | |
180 | wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR | |
181 | " - (EAP) retransWhile --> 0", | |
182 | MAC2STR(state->addr)); | |
183 | } | |
184 | } | |
185 | ||
6fc6879b JM |
186 | eapol_sm_step_run(state); |
187 | ||
188 | eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); | |
189 | } | |
190 | ||
191 | ||
192 | ||
193 | /* Authenticator PAE state machine */ | |
194 | ||
195 | SM_STATE(AUTH_PAE, INITIALIZE) | |
196 | { | |
197 | SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); | |
198 | sm->portMode = Auto; | |
199 | } | |
200 | ||
201 | ||
202 | SM_STATE(AUTH_PAE, DISCONNECTED) | |
203 | { | |
204 | int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; | |
205 | ||
206 | if (sm->eapolLogoff) { | |
207 | if (sm->auth_pae_state == AUTH_PAE_CONNECTING) | |
208 | sm->authEapLogoffsWhileConnecting++; | |
209 | else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) | |
210 | sm->authAuthEapLogoffWhileAuthenticated++; | |
211 | } | |
212 | ||
213 | SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); | |
214 | ||
215 | sm->authPortStatus = Unauthorized; | |
216 | setPortUnauthorized(); | |
217 | sm->reAuthCount = 0; | |
218 | sm->eapolLogoff = FALSE; | |
219 | if (!from_initialize) { | |
220 | sm->eapol->cb.finished(sm->hapd, sm->sta, 0, | |
221 | sm->flags & EAPOL_SM_PREAUTH); | |
222 | } | |
223 | } | |
224 | ||
225 | ||
226 | SM_STATE(AUTH_PAE, RESTART) | |
227 | { | |
228 | if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { | |
229 | if (sm->reAuthenticate) | |
230 | sm->authAuthReauthsWhileAuthenticated++; | |
231 | if (sm->eapolStart) | |
232 | sm->authAuthEapStartsWhileAuthenticated++; | |
233 | if (sm->eapolLogoff) | |
234 | sm->authAuthEapLogoffWhileAuthenticated++; | |
235 | } | |
236 | ||
237 | SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); | |
238 | ||
239 | sm->eap_if->eapRestart = TRUE; | |
240 | } | |
241 | ||
242 | ||
243 | SM_STATE(AUTH_PAE, CONNECTING) | |
244 | { | |
245 | if (sm->auth_pae_state != AUTH_PAE_CONNECTING) | |
246 | sm->authEntersConnecting++; | |
247 | ||
248 | SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); | |
249 | ||
250 | sm->reAuthenticate = FALSE; | |
251 | sm->reAuthCount++; | |
252 | } | |
253 | ||
254 | ||
255 | SM_STATE(AUTH_PAE, HELD) | |
256 | { | |
257 | if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) | |
258 | sm->authAuthFailWhileAuthenticating++; | |
259 | ||
260 | SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); | |
261 | ||
262 | sm->authPortStatus = Unauthorized; | |
263 | setPortUnauthorized(); | |
264 | sm->quietWhile = sm->quietPeriod; | |
265 | sm->eapolLogoff = FALSE; | |
266 | ||
267 | eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, | |
268 | "authentication failed - EAP type: %d (%s)", | |
269 | sm->eap_type_authsrv, | |
270 | eap_type_text(sm->eap_type_authsrv)); | |
271 | if (sm->eap_type_authsrv != sm->eap_type_supp) { | |
272 | eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, | |
273 | "Supplicant used different EAP type: " | |
274 | "%d (%s)", sm->eap_type_supp, | |
275 | eap_type_text(sm->eap_type_supp)); | |
276 | } | |
277 | sm->eapol->cb.finished(sm->hapd, sm->sta, 0, | |
278 | sm->flags & EAPOL_SM_PREAUTH); | |
279 | } | |
280 | ||
281 | ||
282 | SM_STATE(AUTH_PAE, AUTHENTICATED) | |
283 | { | |
284 | char *extra = ""; | |
285 | ||
286 | if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) | |
287 | sm->authAuthSuccessesWhileAuthenticating++; | |
288 | ||
289 | SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); | |
290 | ||
291 | sm->authPortStatus = Authorized; | |
292 | setPortAuthorized(); | |
293 | sm->reAuthCount = 0; | |
294 | if (sm->flags & EAPOL_SM_PREAUTH) | |
295 | extra = " (pre-authentication)"; | |
296 | else if (wpa_auth_sta_get_pmksa(sm->sta->wpa_sm)) | |
297 | extra = " (PMKSA cache)"; | |
298 | eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, | |
299 | "authenticated - EAP type: %d (%s)%s", | |
300 | sm->eap_type_authsrv, | |
301 | eap_type_text(sm->eap_type_authsrv), extra); | |
302 | sm->eapol->cb.finished(sm->hapd, sm->sta, 1, | |
303 | sm->flags & EAPOL_SM_PREAUTH); | |
304 | } | |
305 | ||
306 | ||
307 | SM_STATE(AUTH_PAE, AUTHENTICATING) | |
308 | { | |
309 | SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); | |
310 | ||
311 | sm->eapolStart = FALSE; | |
312 | sm->authSuccess = FALSE; | |
313 | sm->authFail = FALSE; | |
314 | sm->authTimeout = FALSE; | |
315 | sm->authStart = TRUE; | |
316 | sm->keyRun = FALSE; | |
317 | sm->keyDone = FALSE; | |
318 | } | |
319 | ||
320 | ||
321 | SM_STATE(AUTH_PAE, ABORTING) | |
322 | { | |
323 | if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { | |
324 | if (sm->authTimeout) | |
325 | sm->authAuthTimeoutsWhileAuthenticating++; | |
326 | if (sm->eapolStart) | |
327 | sm->authAuthEapStartsWhileAuthenticating++; | |
328 | if (sm->eapolLogoff) | |
329 | sm->authAuthEapLogoffWhileAuthenticating++; | |
330 | } | |
331 | ||
332 | SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); | |
333 | ||
334 | sm->authAbort = TRUE; | |
335 | sm->keyRun = FALSE; | |
336 | sm->keyDone = FALSE; | |
337 | } | |
338 | ||
339 | ||
340 | SM_STATE(AUTH_PAE, FORCE_AUTH) | |
341 | { | |
342 | SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); | |
343 | ||
344 | sm->authPortStatus = Authorized; | |
345 | setPortAuthorized(); | |
346 | sm->portMode = ForceAuthorized; | |
347 | sm->eapolStart = FALSE; | |
348 | txCannedSuccess(); | |
349 | } | |
350 | ||
351 | ||
352 | SM_STATE(AUTH_PAE, FORCE_UNAUTH) | |
353 | { | |
354 | SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); | |
355 | ||
356 | sm->authPortStatus = Unauthorized; | |
357 | setPortUnauthorized(); | |
358 | sm->portMode = ForceUnauthorized; | |
359 | sm->eapolStart = FALSE; | |
360 | txCannedFail(); | |
361 | } | |
362 | ||
363 | ||
364 | SM_STEP(AUTH_PAE) | |
365 | { | |
366 | if ((sm->portControl == Auto && sm->portMode != sm->portControl) || | |
367 | sm->initialize || !sm->eap_if->portEnabled) | |
74bd7dae | 368 | SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); |
6fc6879b JM |
369 | else if (sm->portControl == ForceAuthorized && |
370 | sm->portMode != sm->portControl && | |
371 | !(sm->initialize || !sm->eap_if->portEnabled)) | |
74bd7dae | 372 | SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); |
6fc6879b JM |
373 | else if (sm->portControl == ForceUnauthorized && |
374 | sm->portMode != sm->portControl && | |
375 | !(sm->initialize || !sm->eap_if->portEnabled)) | |
74bd7dae | 376 | SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); |
6fc6879b JM |
377 | else { |
378 | switch (sm->auth_pae_state) { | |
379 | case AUTH_PAE_INITIALIZE: | |
380 | SM_ENTER(AUTH_PAE, DISCONNECTED); | |
381 | break; | |
382 | case AUTH_PAE_DISCONNECTED: | |
383 | SM_ENTER(AUTH_PAE, RESTART); | |
384 | break; | |
385 | case AUTH_PAE_RESTART: | |
386 | if (!sm->eap_if->eapRestart) | |
387 | SM_ENTER(AUTH_PAE, CONNECTING); | |
388 | break; | |
389 | case AUTH_PAE_HELD: | |
390 | if (sm->quietWhile == 0) | |
391 | SM_ENTER(AUTH_PAE, RESTART); | |
392 | break; | |
393 | case AUTH_PAE_CONNECTING: | |
394 | if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) | |
395 | SM_ENTER(AUTH_PAE, DISCONNECTED); | |
396 | else if ((sm->eap_if->eapReq && | |
397 | sm->reAuthCount <= sm->reAuthMax) || | |
398 | sm->eap_if->eapSuccess || sm->eap_if->eapFail) | |
399 | SM_ENTER(AUTH_PAE, AUTHENTICATING); | |
400 | break; | |
401 | case AUTH_PAE_AUTHENTICATED: | |
402 | if (sm->eapolStart || sm->reAuthenticate) | |
403 | SM_ENTER(AUTH_PAE, RESTART); | |
404 | else if (sm->eapolLogoff || !sm->portValid) | |
405 | SM_ENTER(AUTH_PAE, DISCONNECTED); | |
406 | break; | |
407 | case AUTH_PAE_AUTHENTICATING: | |
408 | if (sm->authSuccess && sm->portValid) | |
409 | SM_ENTER(AUTH_PAE, AUTHENTICATED); | |
410 | else if (sm->authFail || | |
411 | (sm->keyDone && !sm->portValid)) | |
412 | SM_ENTER(AUTH_PAE, HELD); | |
413 | else if (sm->eapolStart || sm->eapolLogoff || | |
414 | sm->authTimeout) | |
415 | SM_ENTER(AUTH_PAE, ABORTING); | |
416 | break; | |
417 | case AUTH_PAE_ABORTING: | |
418 | if (sm->eapolLogoff && !sm->authAbort) | |
419 | SM_ENTER(AUTH_PAE, DISCONNECTED); | |
420 | else if (!sm->eapolLogoff && !sm->authAbort) | |
421 | SM_ENTER(AUTH_PAE, RESTART); | |
422 | break; | |
423 | case AUTH_PAE_FORCE_AUTH: | |
424 | if (sm->eapolStart) | |
425 | SM_ENTER(AUTH_PAE, FORCE_AUTH); | |
426 | break; | |
427 | case AUTH_PAE_FORCE_UNAUTH: | |
428 | if (sm->eapolStart) | |
429 | SM_ENTER(AUTH_PAE, FORCE_UNAUTH); | |
430 | break; | |
431 | } | |
432 | } | |
433 | } | |
434 | ||
435 | ||
436 | ||
437 | /* Backend Authentication state machine */ | |
438 | ||
439 | SM_STATE(BE_AUTH, INITIALIZE) | |
440 | { | |
441 | SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); | |
442 | ||
443 | abortAuth(); | |
444 | sm->eap_if->eapNoReq = FALSE; | |
445 | sm->authAbort = FALSE; | |
446 | } | |
447 | ||
448 | ||
449 | SM_STATE(BE_AUTH, REQUEST) | |
450 | { | |
451 | SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); | |
452 | ||
453 | txReq(); | |
454 | sm->eap_if->eapReq = FALSE; | |
455 | sm->backendOtherRequestsToSupplicant++; | |
456 | ||
457 | /* | |
458 | * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but | |
459 | * it looks like this would be logical thing to do there since the old | |
460 | * EAP response would not be valid anymore after the new EAP request | |
461 | * was sent out. | |
462 | * | |
463 | * A race condition has been reported, in which hostapd ended up | |
464 | * sending out EAP-Response/Identity as a response to the first | |
465 | * EAP-Request from the main EAP method. This can be avoided by | |
466 | * clearing eapolEap here. | |
467 | */ | |
468 | sm->eapolEap = FALSE; | |
469 | } | |
470 | ||
471 | ||
472 | SM_STATE(BE_AUTH, RESPONSE) | |
473 | { | |
474 | SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); | |
475 | ||
476 | sm->authTimeout = FALSE; | |
477 | sm->eapolEap = FALSE; | |
478 | sm->eap_if->eapNoReq = FALSE; | |
479 | sm->aWhile = sm->serverTimeout; | |
480 | sm->eap_if->eapResp = TRUE; | |
481 | /* sendRespToServer(); */ | |
482 | sm->backendResponses++; | |
483 | } | |
484 | ||
485 | ||
486 | SM_STATE(BE_AUTH, SUCCESS) | |
487 | { | |
488 | SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); | |
489 | ||
490 | txReq(); | |
491 | sm->authSuccess = TRUE; | |
492 | sm->keyRun = TRUE; | |
493 | } | |
494 | ||
495 | ||
496 | SM_STATE(BE_AUTH, FAIL) | |
497 | { | |
498 | SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); | |
499 | ||
500 | txReq(); | |
501 | sm->authFail = TRUE; | |
502 | } | |
503 | ||
504 | ||
505 | SM_STATE(BE_AUTH, TIMEOUT) | |
506 | { | |
507 | SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); | |
508 | ||
509 | sm->authTimeout = TRUE; | |
510 | } | |
511 | ||
512 | ||
513 | SM_STATE(BE_AUTH, IDLE) | |
514 | { | |
515 | SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); | |
516 | ||
517 | sm->authStart = FALSE; | |
518 | } | |
519 | ||
520 | ||
521 | SM_STATE(BE_AUTH, IGNORE) | |
522 | { | |
523 | SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); | |
524 | ||
525 | sm->eap_if->eapNoReq = FALSE; | |
526 | } | |
527 | ||
528 | ||
529 | SM_STEP(BE_AUTH) | |
530 | { | |
531 | if (sm->portControl != Auto || sm->initialize || sm->authAbort) { | |
74bd7dae | 532 | SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); |
6fc6879b JM |
533 | return; |
534 | } | |
535 | ||
536 | switch (sm->be_auth_state) { | |
537 | case BE_AUTH_INITIALIZE: | |
538 | SM_ENTER(BE_AUTH, IDLE); | |
539 | break; | |
540 | case BE_AUTH_REQUEST: | |
541 | if (sm->eapolEap) | |
542 | SM_ENTER(BE_AUTH, RESPONSE); | |
543 | else if (sm->eap_if->eapReq) | |
544 | SM_ENTER(BE_AUTH, REQUEST); | |
545 | else if (sm->eap_if->eapTimeout) | |
546 | SM_ENTER(BE_AUTH, TIMEOUT); | |
547 | break; | |
548 | case BE_AUTH_RESPONSE: | |
549 | if (sm->eap_if->eapNoReq) | |
550 | SM_ENTER(BE_AUTH, IGNORE); | |
551 | if (sm->eap_if->eapReq) { | |
552 | sm->backendAccessChallenges++; | |
553 | SM_ENTER(BE_AUTH, REQUEST); | |
554 | } else if (sm->aWhile == 0) | |
555 | SM_ENTER(BE_AUTH, TIMEOUT); | |
556 | else if (sm->eap_if->eapFail) { | |
557 | sm->backendAuthFails++; | |
558 | SM_ENTER(BE_AUTH, FAIL); | |
559 | } else if (sm->eap_if->eapSuccess) { | |
560 | sm->backendAuthSuccesses++; | |
561 | SM_ENTER(BE_AUTH, SUCCESS); | |
562 | } | |
563 | break; | |
564 | case BE_AUTH_SUCCESS: | |
565 | SM_ENTER(BE_AUTH, IDLE); | |
566 | break; | |
567 | case BE_AUTH_FAIL: | |
568 | SM_ENTER(BE_AUTH, IDLE); | |
569 | break; | |
570 | case BE_AUTH_TIMEOUT: | |
571 | SM_ENTER(BE_AUTH, IDLE); | |
572 | break; | |
573 | case BE_AUTH_IDLE: | |
574 | if (sm->eap_if->eapFail && sm->authStart) | |
575 | SM_ENTER(BE_AUTH, FAIL); | |
576 | else if (sm->eap_if->eapReq && sm->authStart) | |
577 | SM_ENTER(BE_AUTH, REQUEST); | |
578 | else if (sm->eap_if->eapSuccess && sm->authStart) | |
579 | SM_ENTER(BE_AUTH, SUCCESS); | |
580 | break; | |
581 | case BE_AUTH_IGNORE: | |
582 | if (sm->eapolEap) | |
583 | SM_ENTER(BE_AUTH, RESPONSE); | |
584 | else if (sm->eap_if->eapReq) | |
585 | SM_ENTER(BE_AUTH, REQUEST); | |
586 | else if (sm->eap_if->eapTimeout) | |
587 | SM_ENTER(BE_AUTH, TIMEOUT); | |
588 | break; | |
589 | } | |
590 | } | |
591 | ||
592 | ||
593 | ||
594 | /* Reauthentication Timer state machine */ | |
595 | ||
596 | SM_STATE(REAUTH_TIMER, INITIALIZE) | |
597 | { | |
598 | SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); | |
599 | ||
600 | sm->reAuthWhen = sm->reAuthPeriod; | |
601 | } | |
602 | ||
603 | ||
604 | SM_STATE(REAUTH_TIMER, REAUTHENTICATE) | |
605 | { | |
606 | SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); | |
607 | ||
608 | sm->reAuthenticate = TRUE; | |
609 | wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL); | |
610 | } | |
611 | ||
612 | ||
613 | SM_STEP(REAUTH_TIMER) | |
614 | { | |
615 | if (sm->portControl != Auto || sm->initialize || | |
616 | sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { | |
74bd7dae | 617 | SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); |
6fc6879b JM |
618 | return; |
619 | } | |
620 | ||
621 | switch (sm->reauth_timer_state) { | |
622 | case REAUTH_TIMER_INITIALIZE: | |
623 | if (sm->reAuthWhen == 0) | |
624 | SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); | |
625 | break; | |
626 | case REAUTH_TIMER_REAUTHENTICATE: | |
627 | SM_ENTER(REAUTH_TIMER, INITIALIZE); | |
628 | break; | |
629 | } | |
630 | } | |
631 | ||
632 | ||
633 | ||
634 | /* Authenticator Key Transmit state machine */ | |
635 | ||
636 | SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) | |
637 | { | |
638 | SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); | |
639 | } | |
640 | ||
641 | ||
642 | SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) | |
643 | { | |
644 | SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); | |
645 | ||
646 | txKey(); | |
647 | sm->eap_if->eapKeyAvailable = FALSE; | |
648 | sm->keyDone = TRUE; | |
649 | } | |
650 | ||
651 | ||
652 | SM_STEP(AUTH_KEY_TX) | |
653 | { | |
654 | if (sm->initialize || sm->portControl != Auto) { | |
74bd7dae | 655 | SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); |
6fc6879b JM |
656 | return; |
657 | } | |
658 | ||
659 | switch (sm->auth_key_tx_state) { | |
660 | case AUTH_KEY_TX_NO_KEY_TRANSMIT: | |
661 | if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && | |
662 | sm->keyRun && !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) | |
663 | SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); | |
664 | break; | |
665 | case AUTH_KEY_TX_KEY_TRANSMIT: | |
666 | if (!sm->keyTxEnabled || !sm->keyRun) | |
667 | SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); | |
668 | else if (sm->eap_if->eapKeyAvailable) | |
669 | SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); | |
670 | break; | |
671 | } | |
672 | } | |
673 | ||
674 | ||
675 | ||
676 | /* Key Receive state machine */ | |
677 | ||
678 | SM_STATE(KEY_RX, NO_KEY_RECEIVE) | |
679 | { | |
680 | SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); | |
681 | } | |
682 | ||
683 | ||
684 | SM_STATE(KEY_RX, KEY_RECEIVE) | |
685 | { | |
686 | SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); | |
687 | ||
688 | processKey(); | |
689 | sm->rxKey = FALSE; | |
690 | } | |
691 | ||
692 | ||
693 | SM_STEP(KEY_RX) | |
694 | { | |
695 | if (sm->initialize || !sm->eap_if->portEnabled) { | |
74bd7dae | 696 | SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); |
6fc6879b JM |
697 | return; |
698 | } | |
699 | ||
700 | switch (sm->key_rx_state) { | |
701 | case KEY_RX_NO_KEY_RECEIVE: | |
702 | if (sm->rxKey) | |
703 | SM_ENTER(KEY_RX, KEY_RECEIVE); | |
704 | break; | |
705 | case KEY_RX_KEY_RECEIVE: | |
706 | if (sm->rxKey) | |
707 | SM_ENTER(KEY_RX, KEY_RECEIVE); | |
708 | break; | |
709 | } | |
710 | } | |
711 | ||
712 | ||
713 | ||
714 | /* Controlled Directions state machine */ | |
715 | ||
716 | SM_STATE(CTRL_DIR, FORCE_BOTH) | |
717 | { | |
718 | SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); | |
719 | sm->operControlledDirections = Both; | |
720 | } | |
721 | ||
722 | ||
723 | SM_STATE(CTRL_DIR, IN_OR_BOTH) | |
724 | { | |
725 | SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); | |
726 | sm->operControlledDirections = sm->adminControlledDirections; | |
727 | } | |
728 | ||
729 | ||
730 | SM_STEP(CTRL_DIR) | |
731 | { | |
732 | if (sm->initialize) { | |
74bd7dae | 733 | SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); |
6fc6879b JM |
734 | return; |
735 | } | |
736 | ||
737 | switch (sm->ctrl_dir_state) { | |
738 | case CTRL_DIR_FORCE_BOTH: | |
739 | if (sm->eap_if->portEnabled && sm->operEdge) | |
740 | SM_ENTER(CTRL_DIR, IN_OR_BOTH); | |
741 | break; | |
742 | case CTRL_DIR_IN_OR_BOTH: | |
743 | if (sm->operControlledDirections != | |
744 | sm->adminControlledDirections) | |
745 | SM_ENTER(CTRL_DIR, IN_OR_BOTH); | |
746 | if (!sm->eap_if->portEnabled || !sm->operEdge) | |
747 | SM_ENTER(CTRL_DIR, FORCE_BOTH); | |
748 | break; | |
749 | } | |
750 | } | |
751 | ||
752 | ||
753 | ||
754 | struct eapol_state_machine * | |
755 | eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, | |
756 | int preauth, struct sta_info *sta) | |
757 | { | |
758 | struct eapol_state_machine *sm; | |
759 | struct hostapd_data *hapd; /* TODO: to be removed */ | |
760 | struct eap_config eap_conf; | |
761 | ||
762 | if (eapol == NULL) | |
763 | return NULL; | |
764 | hapd = eapol->conf.hapd; | |
765 | ||
766 | sm = os_zalloc(sizeof(*sm)); | |
767 | if (sm == NULL) { | |
768 | wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " | |
769 | "failed"); | |
770 | return NULL; | |
771 | } | |
772 | sm->radius_identifier = -1; | |
773 | os_memcpy(sm->addr, addr, ETH_ALEN); | |
774 | if (preauth) | |
775 | sm->flags |= EAPOL_SM_PREAUTH; | |
776 | ||
777 | sm->hapd = hapd; | |
778 | sm->eapol = eapol; | |
779 | sm->sta = sta; | |
780 | ||
781 | /* Set default values for state machine constants */ | |
782 | sm->auth_pae_state = AUTH_PAE_INITIALIZE; | |
783 | sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; | |
784 | sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; | |
785 | ||
786 | sm->be_auth_state = BE_AUTH_INITIALIZE; | |
787 | sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; | |
788 | ||
789 | sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; | |
790 | sm->reAuthPeriod = eapol->conf.eap_reauth_period; | |
791 | sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; | |
792 | ||
793 | sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; | |
794 | ||
795 | sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; | |
796 | ||
797 | sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; | |
798 | ||
799 | sm->portControl = Auto; | |
800 | ||
801 | if (!eapol->conf.wpa && | |
802 | (hapd->default_wep_key || eapol->conf.individual_wep_key_len > 0)) | |
803 | sm->keyTxEnabled = TRUE; | |
804 | else | |
805 | sm->keyTxEnabled = FALSE; | |
806 | if (eapol->conf.wpa) | |
807 | sm->portValid = FALSE; | |
808 | else | |
809 | sm->portValid = TRUE; | |
810 | ||
811 | os_memset(&eap_conf, 0, sizeof(eap_conf)); | |
812 | eap_conf.eap_server = eapol->conf.eap_server; | |
813 | eap_conf.ssl_ctx = eapol->conf.ssl_ctx; | |
814 | eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; | |
815 | eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; | |
816 | eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; | |
2d867244 JM |
817 | eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; |
818 | eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; | |
378eae5e | 819 | eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; |
a11c90a6 JM |
820 | eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; |
821 | eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; | |
6fc6879b | 822 | eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; |
c3e258ae | 823 | eap_conf.tnc = eapol->conf.tnc; |
ad08c363 | 824 | eap_conf.wps = eapol->conf.wps; |
eb76b7e3 | 825 | eap_conf.assoc_wps_ie = sta->wps_ie; |
6fc6879b JM |
826 | sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); |
827 | if (sm->eap == NULL) { | |
828 | eapol_auth_free(sm); | |
829 | return NULL; | |
830 | } | |
831 | sm->eap_if = eap_get_interface(sm->eap); | |
832 | ||
833 | eapol_auth_initialize(sm); | |
834 | ||
835 | return sm; | |
836 | } | |
837 | ||
838 | ||
839 | void eapol_auth_free(struct eapol_state_machine *sm) | |
840 | { | |
841 | if (sm == NULL) | |
842 | return; | |
843 | ||
844 | eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); | |
845 | eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); | |
846 | if (sm->eap) | |
847 | eap_server_sm_deinit(sm->eap); | |
848 | os_free(sm); | |
849 | } | |
850 | ||
851 | ||
852 | static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, | |
853 | const u8 *addr) | |
854 | { | |
855 | return eapol->cb.sta_entry_alive(eapol->conf.hapd, addr); | |
856 | } | |
857 | ||
858 | ||
859 | static void eapol_sm_step_run(struct eapol_state_machine *sm) | |
860 | { | |
861 | struct eapol_authenticator *eapol = sm->eapol; | |
862 | u8 addr[ETH_ALEN]; | |
863 | unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, | |
864 | prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; | |
865 | int max_steps = 100; | |
866 | ||
867 | os_memcpy(addr, sm->addr, ETH_ALEN); | |
868 | ||
869 | /* | |
870 | * Allow EAPOL state machines to run as long as there are state | |
871 | * changes, but exit and return here through event loop if more than | |
872 | * 100 steps is needed as a precaution against infinite loops inside | |
873 | * eloop callback. | |
874 | */ | |
875 | restart: | |
876 | prev_auth_pae = sm->auth_pae_state; | |
877 | prev_be_auth = sm->be_auth_state; | |
878 | prev_reauth_timer = sm->reauth_timer_state; | |
879 | prev_auth_key_tx = sm->auth_key_tx_state; | |
880 | prev_key_rx = sm->key_rx_state; | |
881 | prev_ctrl_dir = sm->ctrl_dir_state; | |
882 | ||
883 | SM_STEP_RUN(AUTH_PAE); | |
884 | if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) | |
885 | SM_STEP_RUN(BE_AUTH); | |
886 | if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) | |
887 | SM_STEP_RUN(REAUTH_TIMER); | |
888 | if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) | |
889 | SM_STEP_RUN(AUTH_KEY_TX); | |
890 | if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) | |
891 | SM_STEP_RUN(KEY_RX); | |
892 | if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) | |
893 | SM_STEP_RUN(CTRL_DIR); | |
894 | ||
895 | if (prev_auth_pae != sm->auth_pae_state || | |
896 | prev_be_auth != sm->be_auth_state || | |
897 | prev_reauth_timer != sm->reauth_timer_state || | |
898 | prev_auth_key_tx != sm->auth_key_tx_state || | |
899 | prev_key_rx != sm->key_rx_state || | |
900 | prev_ctrl_dir != sm->ctrl_dir_state) { | |
901 | if (--max_steps > 0) | |
902 | goto restart; | |
903 | /* Re-run from eloop timeout */ | |
904 | eapol_auth_step(sm); | |
905 | return; | |
906 | } | |
907 | ||
908 | if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { | |
909 | if (eap_server_sm_step(sm->eap)) { | |
910 | if (--max_steps > 0) | |
911 | goto restart; | |
912 | /* Re-run from eloop timeout */ | |
913 | eapol_auth_step(sm); | |
914 | return; | |
915 | } | |
916 | ||
917 | /* TODO: find a better location for this */ | |
918 | if (sm->eap_if->aaaEapResp) { | |
919 | sm->eap_if->aaaEapResp = FALSE; | |
920 | if (sm->eap_if->aaaEapRespData == NULL) { | |
921 | wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " | |
922 | "but no aaaEapRespData available"); | |
923 | return; | |
924 | } | |
925 | sm->eapol->cb.aaa_send( | |
926 | sm->hapd, sm->sta, | |
927 | wpabuf_head(sm->eap_if->aaaEapRespData), | |
928 | wpabuf_len(sm->eap_if->aaaEapRespData)); | |
929 | } | |
930 | } | |
931 | ||
932 | if (eapol_sm_sta_entry_alive(eapol, addr)) | |
933 | wpa_auth_sm_notify(sm->sta->wpa_sm); | |
934 | } | |
935 | ||
936 | ||
937 | static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) | |
938 | { | |
939 | struct eapol_state_machine *sm = eloop_ctx; | |
940 | eapol_sm_step_run(sm); | |
941 | } | |
942 | ||
943 | ||
944 | void eapol_auth_step(struct eapol_state_machine *sm) | |
945 | { | |
946 | /* | |
947 | * Run eapol_sm_step_run from a registered timeout to make sure that | |
948 | * other possible timeouts/events are processed and to avoid long | |
949 | * function call chains. | |
950 | */ | |
951 | ||
952 | eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); | |
953 | } | |
954 | ||
955 | ||
956 | void eapol_auth_initialize(struct eapol_state_machine *sm) | |
957 | { | |
958 | sm->initializing = TRUE; | |
959 | /* Initialize the state machines by asserting initialize and then | |
960 | * deasserting it after one step */ | |
961 | sm->initialize = TRUE; | |
962 | eapol_sm_step_run(sm); | |
963 | sm->initialize = FALSE; | |
964 | eapol_sm_step_run(sm); | |
965 | sm->initializing = FALSE; | |
966 | ||
967 | /* Start one second tick for port timers state machine */ | |
968 | eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); | |
969 | eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); | |
970 | } | |
971 | ||
972 | ||
973 | #ifdef HOSTAPD_DUMP_STATE | |
974 | static inline const char * port_type_txt(PortTypes pt) | |
975 | { | |
976 | switch (pt) { | |
977 | case ForceUnauthorized: return "ForceUnauthorized"; | |
978 | case ForceAuthorized: return "ForceAuthorized"; | |
979 | case Auto: return "Auto"; | |
980 | default: return "Unknown"; | |
981 | } | |
982 | } | |
983 | ||
984 | ||
985 | static inline const char * port_state_txt(PortState ps) | |
986 | { | |
987 | switch (ps) { | |
988 | case Unauthorized: return "Unauthorized"; | |
989 | case Authorized: return "Authorized"; | |
990 | default: return "Unknown"; | |
991 | } | |
992 | } | |
993 | ||
994 | ||
995 | static inline const char * ctrl_dir_txt(ControlledDirection dir) | |
996 | { | |
997 | switch (dir) { | |
998 | case Both: return "Both"; | |
999 | case In: return "In"; | |
1000 | default: return "Unknown"; | |
1001 | } | |
1002 | } | |
1003 | ||
1004 | ||
1005 | static inline const char * auth_pae_state_txt(int s) | |
1006 | { | |
1007 | switch (s) { | |
1008 | case AUTH_PAE_INITIALIZE: return "INITIALIZE"; | |
1009 | case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; | |
1010 | case AUTH_PAE_CONNECTING: return "CONNECTING"; | |
1011 | case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; | |
1012 | case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; | |
1013 | case AUTH_PAE_ABORTING: return "ABORTING"; | |
1014 | case AUTH_PAE_HELD: return "HELD"; | |
1015 | case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; | |
1016 | case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; | |
1017 | case AUTH_PAE_RESTART: return "RESTART"; | |
1018 | default: return "Unknown"; | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | ||
1023 | static inline const char * be_auth_state_txt(int s) | |
1024 | { | |
1025 | switch (s) { | |
1026 | case BE_AUTH_REQUEST: return "REQUEST"; | |
1027 | case BE_AUTH_RESPONSE: return "RESPONSE"; | |
1028 | case BE_AUTH_SUCCESS: return "SUCCESS"; | |
1029 | case BE_AUTH_FAIL: return "FAIL"; | |
1030 | case BE_AUTH_TIMEOUT: return "TIMEOUT"; | |
1031 | case BE_AUTH_IDLE: return "IDLE"; | |
1032 | case BE_AUTH_INITIALIZE: return "INITIALIZE"; | |
1033 | case BE_AUTH_IGNORE: return "IGNORE"; | |
1034 | default: return "Unknown"; | |
1035 | } | |
1036 | } | |
1037 | ||
1038 | ||
1039 | static inline const char * reauth_timer_state_txt(int s) | |
1040 | { | |
1041 | switch (s) { | |
1042 | case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; | |
1043 | case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; | |
1044 | default: return "Unknown"; | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | ||
1049 | static inline const char * auth_key_tx_state_txt(int s) | |
1050 | { | |
1051 | switch (s) { | |
1052 | case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; | |
1053 | case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; | |
1054 | default: return "Unknown"; | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | ||
1059 | static inline const char * key_rx_state_txt(int s) | |
1060 | { | |
1061 | switch (s) { | |
1062 | case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; | |
1063 | case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; | |
1064 | default: return "Unknown"; | |
1065 | } | |
1066 | } | |
1067 | ||
1068 | ||
1069 | static inline const char * ctrl_dir_state_txt(int s) | |
1070 | { | |
1071 | switch (s) { | |
1072 | case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; | |
1073 | case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; | |
1074 | default: return "Unknown"; | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | ||
1079 | void eapol_auth_dump_state(FILE *f, const char *prefix, | |
1080 | struct eapol_state_machine *sm) | |
1081 | { | |
1082 | fprintf(f, "%sEAPOL state machine:\n", prefix); | |
1083 | fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, | |
1084 | sm->aWhile, sm->quietWhile, sm->reAuthWhen); | |
1085 | #define _SB(b) ((b) ? "TRUE" : "FALSE") | |
1086 | fprintf(f, | |
1087 | "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" | |
1088 | "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" | |
1089 | "%s eapSuccess=%s eapTimeout=%s initialize=%s " | |
1090 | "keyAvailable=%s\n" | |
1091 | "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" | |
1092 | "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", | |
1093 | prefix, _SB(sm->authAbort), _SB(sm->authFail), | |
1094 | port_state_txt(sm->authPortStatus), _SB(sm->authStart), | |
1095 | prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), | |
1096 | _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), | |
1097 | prefix, _SB(sm->eap_if->eapSuccess), | |
1098 | _SB(sm->eap_if->eapTimeout), | |
1099 | _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), | |
1100 | prefix, _SB(sm->keyDone), _SB(sm->keyRun), | |
1101 | _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), | |
1102 | prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), | |
1103 | _SB(sm->reAuthenticate)); | |
1104 | ||
1105 | fprintf(f, "%s Authenticator PAE:\n" | |
1106 | "%s state=%s\n" | |
1107 | "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" | |
1108 | "%s portMode=%s reAuthCount=%d\n" | |
1109 | "%s quietPeriod=%d reAuthMax=%d\n" | |
1110 | "%s authEntersConnecting=%d\n" | |
1111 | "%s authEapLogoffsWhileConnecting=%d\n" | |
1112 | "%s authEntersAuthenticating=%d\n" | |
1113 | "%s authAuthSuccessesWhileAuthenticating=%d\n" | |
1114 | "%s authAuthTimeoutsWhileAuthenticating=%d\n" | |
1115 | "%s authAuthFailWhileAuthenticating=%d\n" | |
1116 | "%s authAuthEapStartsWhileAuthenticating=%d\n" | |
1117 | "%s authAuthEapLogoffWhileAuthenticating=%d\n" | |
1118 | "%s authAuthReauthsWhileAuthenticated=%d\n" | |
1119 | "%s authAuthEapStartsWhileAuthenticated=%d\n" | |
1120 | "%s authAuthEapLogoffWhileAuthenticated=%d\n", | |
1121 | prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, | |
1122 | _SB(sm->eapolLogoff), _SB(sm->eapolStart), | |
1123 | _SB(sm->eap_if->eapRestart), | |
1124 | prefix, port_type_txt(sm->portMode), sm->reAuthCount, | |
1125 | prefix, sm->quietPeriod, sm->reAuthMax, | |
1126 | prefix, sm->authEntersConnecting, | |
1127 | prefix, sm->authEapLogoffsWhileConnecting, | |
1128 | prefix, sm->authEntersAuthenticating, | |
1129 | prefix, sm->authAuthSuccessesWhileAuthenticating, | |
1130 | prefix, sm->authAuthTimeoutsWhileAuthenticating, | |
1131 | prefix, sm->authAuthFailWhileAuthenticating, | |
1132 | prefix, sm->authAuthEapStartsWhileAuthenticating, | |
1133 | prefix, sm->authAuthEapLogoffWhileAuthenticating, | |
1134 | prefix, sm->authAuthReauthsWhileAuthenticated, | |
1135 | prefix, sm->authAuthEapStartsWhileAuthenticated, | |
1136 | prefix, sm->authAuthEapLogoffWhileAuthenticated); | |
1137 | ||
1138 | fprintf(f, "%s Backend Authentication:\n" | |
1139 | "%s state=%s\n" | |
1140 | "%s eapNoReq=%s eapReq=%s eapResp=%s\n" | |
1141 | "%s serverTimeout=%d\n" | |
1142 | "%s backendResponses=%d\n" | |
1143 | "%s backendAccessChallenges=%d\n" | |
1144 | "%s backendOtherRequestsToSupplicant=%d\n" | |
1145 | "%s backendAuthSuccesses=%d\n" | |
1146 | "%s backendAuthFails=%d\n", | |
1147 | prefix, prefix, | |
1148 | be_auth_state_txt(sm->be_auth_state), | |
1149 | prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), | |
1150 | _SB(sm->eap_if->eapResp), | |
1151 | prefix, sm->serverTimeout, | |
1152 | prefix, sm->backendResponses, | |
1153 | prefix, sm->backendAccessChallenges, | |
1154 | prefix, sm->backendOtherRequestsToSupplicant, | |
1155 | prefix, sm->backendAuthSuccesses, | |
1156 | prefix, sm->backendAuthFails); | |
1157 | ||
1158 | fprintf(f, "%s Reauthentication Timer:\n" | |
1159 | "%s state=%s\n" | |
1160 | "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, | |
1161 | reauth_timer_state_txt(sm->reauth_timer_state), prefix, | |
1162 | sm->reAuthPeriod, _SB(sm->reAuthEnabled)); | |
1163 | ||
1164 | fprintf(f, "%s Authenticator Key Transmit:\n" | |
1165 | "%s state=%s\n", prefix, prefix, | |
1166 | auth_key_tx_state_txt(sm->auth_key_tx_state)); | |
1167 | ||
1168 | fprintf(f, "%s Key Receive:\n" | |
1169 | "%s state=%s\n" | |
1170 | "%s rxKey=%s\n", prefix, prefix, | |
1171 | key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); | |
1172 | ||
1173 | fprintf(f, "%s Controlled Directions:\n" | |
1174 | "%s state=%s\n" | |
1175 | "%s adminControlledDirections=%s " | |
1176 | "operControlledDirections=%s\n" | |
1177 | "%s operEdge=%s\n", prefix, prefix, | |
1178 | ctrl_dir_state_txt(sm->ctrl_dir_state), | |
1179 | prefix, ctrl_dir_txt(sm->adminControlledDirections), | |
1180 | ctrl_dir_txt(sm->operControlledDirections), | |
1181 | prefix, _SB(sm->operEdge)); | |
1182 | #undef _SB | |
1183 | } | |
1184 | #endif /* HOSTAPD_DUMP_STATE */ | |
1185 | ||
1186 | ||
1187 | static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, | |
1188 | size_t identity_len, int phase2, | |
1189 | struct eap_user *user) | |
1190 | { | |
1191 | struct eapol_state_machine *sm = ctx; | |
1192 | return sm->eapol->cb.get_eap_user(sm->hapd, identity, identity_len, | |
1193 | phase2, user); | |
1194 | } | |
1195 | ||
1196 | ||
1197 | static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) | |
1198 | { | |
1199 | struct eapol_state_machine *sm = ctx; | |
1200 | *len = sm->eapol->conf.eap_req_id_text_len; | |
1201 | return sm->eapol->conf.eap_req_id_text; | |
1202 | } | |
1203 | ||
1204 | ||
1205 | static struct eapol_callbacks eapol_cb = | |
1206 | { | |
1207 | .get_eap_user = eapol_sm_get_eap_user, | |
1208 | .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, | |
1209 | }; | |
1210 | ||
1211 | ||
1212 | int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) | |
1213 | { | |
1214 | if (sm == NULL || ctx != sm->eap) | |
1215 | return -1; | |
1216 | ||
1217 | eap_sm_pending_cb(sm->eap); | |
1218 | eapol_auth_step(sm); | |
1219 | ||
1220 | return 0; | |
1221 | } | |
1222 | ||
1223 | ||
1224 | static int eapol_auth_conf_clone(struct eapol_auth_config *dst, | |
1225 | struct eapol_auth_config *src) | |
1226 | { | |
1227 | dst->hapd = src->hapd; | |
1228 | dst->eap_reauth_period = src->eap_reauth_period; | |
1229 | dst->wpa = src->wpa; | |
1230 | dst->individual_wep_key_len = src->individual_wep_key_len; | |
1231 | dst->eap_server = src->eap_server; | |
1232 | dst->ssl_ctx = src->ssl_ctx; | |
1233 | dst->eap_sim_db_priv = src->eap_sim_db_priv; | |
1234 | os_free(dst->eap_req_id_text); | |
1235 | if (src->eap_req_id_text) { | |
1236 | dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); | |
1237 | if (dst->eap_req_id_text == NULL) | |
1238 | return -1; | |
1239 | os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, | |
1240 | src->eap_req_id_text_len); | |
1241 | dst->eap_req_id_text_len = src->eap_req_id_text_len; | |
1242 | } else { | |
1243 | dst->eap_req_id_text = NULL; | |
1244 | dst->eap_req_id_text_len = 0; | |
1245 | } | |
1246 | if (src->pac_opaque_encr_key) { | |
1247 | dst->pac_opaque_encr_key = os_malloc(16); | |
1248 | os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, | |
1249 | 16); | |
1250 | } else | |
1251 | dst->pac_opaque_encr_key = NULL; | |
2d867244 JM |
1252 | if (src->eap_fast_a_id) { |
1253 | dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); | |
1254 | if (dst->eap_fast_a_id == NULL) { | |
1255 | os_free(dst->eap_req_id_text); | |
1256 | return -1; | |
1257 | } | |
1258 | os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, | |
1259 | src->eap_fast_a_id_len); | |
1260 | dst->eap_fast_a_id_len = src->eap_fast_a_id_len; | |
1261 | } else | |
6fc6879b | 1262 | dst->eap_fast_a_id = NULL; |
2d867244 JM |
1263 | if (src->eap_fast_a_id_info) { |
1264 | dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); | |
1265 | if (dst->eap_fast_a_id_info == NULL) { | |
1266 | os_free(dst->eap_req_id_text); | |
1267 | os_free(dst->eap_fast_a_id); | |
1268 | return -1; | |
1269 | } | |
1270 | } else | |
1271 | dst->eap_fast_a_id_info = NULL; | |
378eae5e | 1272 | dst->eap_fast_prov = src->eap_fast_prov; |
a11c90a6 JM |
1273 | dst->pac_key_lifetime = src->pac_key_lifetime; |
1274 | dst->pac_key_refresh_time = src->pac_key_refresh_time; | |
6fc6879b | 1275 | dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; |
c3e258ae | 1276 | dst->tnc = src->tnc; |
ad08c363 | 1277 | dst->wps = src->wps; |
6fc6879b JM |
1278 | return 0; |
1279 | } | |
1280 | ||
1281 | ||
1282 | static void eapol_auth_conf_free(struct eapol_auth_config *conf) | |
1283 | { | |
1284 | os_free(conf->eap_req_id_text); | |
1285 | conf->eap_req_id_text = NULL; | |
1286 | os_free(conf->pac_opaque_encr_key); | |
1287 | conf->pac_opaque_encr_key = NULL; | |
1288 | os_free(conf->eap_fast_a_id); | |
1289 | conf->eap_fast_a_id = NULL; | |
2d867244 JM |
1290 | os_free(conf->eap_fast_a_id_info); |
1291 | conf->eap_fast_a_id_info = NULL; | |
6fc6879b JM |
1292 | } |
1293 | ||
1294 | ||
1295 | struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, | |
1296 | struct eapol_auth_cb *cb) | |
1297 | { | |
1298 | struct eapol_authenticator *eapol; | |
1299 | ||
1300 | eapol = os_zalloc(sizeof(*eapol)); | |
1301 | if (eapol == NULL) | |
1302 | return NULL; | |
1303 | ||
1304 | if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { | |
1305 | os_free(eapol); | |
1306 | return NULL; | |
1307 | } | |
1308 | ||
1309 | eapol->cb.eapol_send = cb->eapol_send; | |
1310 | eapol->cb.aaa_send = cb->aaa_send; | |
1311 | eapol->cb.finished = cb->finished; | |
1312 | eapol->cb.get_eap_user = cb->get_eap_user; | |
1313 | eapol->cb.sta_entry_alive = cb->sta_entry_alive; | |
1314 | eapol->cb.logger = cb->logger; | |
1315 | eapol->cb.set_port_authorized = cb->set_port_authorized; | |
1316 | eapol->cb.abort_auth = cb->abort_auth; | |
1317 | eapol->cb.tx_key = cb->tx_key; | |
1318 | ||
1319 | return eapol; | |
1320 | } | |
1321 | ||
1322 | ||
1323 | void eapol_auth_deinit(struct eapol_authenticator *eapol) | |
1324 | { | |
1325 | if (eapol == NULL) | |
1326 | return; | |
1327 | ||
1328 | eapol_auth_conf_free(&eapol->conf); | |
1329 | os_free(eapol); | |
1330 | } |