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