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