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