/src/hostap/src/eap_server/eap_server.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * hostapd / EAP Full Authenticator state machine (RFC 4137) |
3 | | * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | * |
8 | | * This state machine is based on the full authenticator state machine defined |
9 | | * in RFC 4137. However, to support backend authentication in RADIUS |
10 | | * authentication server functionality, parts of backend authenticator (also |
11 | | * from RFC 4137) are mixed in. This functionality is enabled by setting |
12 | | * backend_auth configuration variable to true. |
13 | | */ |
14 | | |
15 | | #include "includes.h" |
16 | | |
17 | | #include "common.h" |
18 | | #include "crypto/sha256.h" |
19 | | #include "eap_i.h" |
20 | | #include "state_machine.h" |
21 | | #include "common/wpa_ctrl.h" |
22 | | |
23 | | #define STATE_MACHINE_DATA struct eap_sm |
24 | 0 | #define STATE_MACHINE_DEBUG_PREFIX "EAP" |
25 | | |
26 | | /* EAP state machines are described in RFC 4137 */ |
27 | | |
28 | | static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, |
29 | | int eapSRTT, int eapRTTVAR, |
30 | | int methodTimeout); |
31 | | static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp); |
32 | | static int eap_sm_getId(const struct wpabuf *data); |
33 | | static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id); |
34 | | static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id); |
35 | | static int eap_sm_nextId(struct eap_sm *sm, int id); |
36 | | static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, |
37 | | size_t len); |
38 | | static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, |
39 | | int *vendor); |
40 | | static int eap_sm_Policy_getDecision(struct eap_sm *sm); |
41 | | static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method); |
42 | | |
43 | | |
44 | | static int eap_get_erp_send_reauth_start(struct eap_sm *sm) |
45 | 0 | { |
46 | 0 | if (sm->eapol_cb->get_erp_send_reauth_start) |
47 | 0 | return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx); |
48 | 0 | return 0; |
49 | 0 | } |
50 | | |
51 | | |
52 | | static const char * eap_get_erp_domain(struct eap_sm *sm) |
53 | 0 | { |
54 | 0 | if (sm->eapol_cb->get_erp_domain) |
55 | 0 | return sm->eapol_cb->get_erp_domain(sm->eapol_ctx); |
56 | 0 | return NULL; |
57 | 0 | } |
58 | | |
59 | | |
60 | | #ifdef CONFIG_ERP |
61 | | |
62 | | static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm, |
63 | | const char *keyname) |
64 | | { |
65 | | if (sm->eapol_cb->erp_get_key) |
66 | | return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname); |
67 | | return NULL; |
68 | | } |
69 | | |
70 | | |
71 | | static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp) |
72 | | { |
73 | | if (sm->eapol_cb->erp_add_key) |
74 | | return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp); |
75 | | return -1; |
76 | | } |
77 | | |
78 | | #endif /* CONFIG_ERP */ |
79 | | |
80 | | |
81 | | static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, |
82 | | u8 id) |
83 | 0 | { |
84 | 0 | const char *domain; |
85 | 0 | size_t plen = 1; |
86 | 0 | struct wpabuf *msg; |
87 | 0 | size_t domain_len = 0; |
88 | |
|
89 | 0 | domain = eap_get_erp_domain(sm); |
90 | 0 | if (domain) { |
91 | 0 | domain_len = os_strlen(domain); |
92 | 0 | plen += 2 + domain_len; |
93 | 0 | } |
94 | |
|
95 | 0 | msg = eap_msg_alloc(EAP_VENDOR_IETF, |
96 | 0 | (enum eap_type) EAP_ERP_TYPE_REAUTH_START, plen, |
97 | 0 | EAP_CODE_INITIATE, id); |
98 | 0 | if (msg == NULL) |
99 | 0 | return NULL; |
100 | 0 | wpabuf_put_u8(msg, 0); /* Reserved */ |
101 | 0 | if (domain) { |
102 | | /* Domain name TLV */ |
103 | 0 | wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME); |
104 | 0 | wpabuf_put_u8(msg, domain_len); |
105 | 0 | wpabuf_put_data(msg, domain, domain_len); |
106 | 0 | } |
107 | |
|
108 | 0 | return msg; |
109 | 0 | } |
110 | | |
111 | | |
112 | | static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) |
113 | 0 | { |
114 | 0 | if (src == NULL) |
115 | 0 | return -1; |
116 | | |
117 | 0 | wpabuf_free(*dst); |
118 | 0 | *dst = wpabuf_dup(src); |
119 | 0 | return *dst ? 0 : -1; |
120 | 0 | } |
121 | | |
122 | | |
123 | | static int eap_copy_data(u8 **dst, size_t *dst_len, |
124 | | const u8 *src, size_t src_len) |
125 | 0 | { |
126 | 0 | if (src == NULL) |
127 | 0 | return -1; |
128 | | |
129 | 0 | os_free(*dst); |
130 | 0 | *dst = os_malloc(src_len); |
131 | 0 | if (*dst) { |
132 | 0 | os_memcpy(*dst, src, src_len); |
133 | 0 | *dst_len = src_len; |
134 | 0 | return 0; |
135 | 0 | } else { |
136 | 0 | *dst_len = 0; |
137 | 0 | return -1; |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | #define EAP_COPY(dst, src) \ |
142 | 0 | eap_copy_data((dst), (dst ## Len), (src), (src ## Len)) |
143 | | |
144 | | |
145 | | /** |
146 | | * eap_user_get - Fetch user information from the database |
147 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
148 | | * @identity: Identity (User-Name) of the user |
149 | | * @identity_len: Length of identity in bytes |
150 | | * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user |
151 | | * Returns: 0 on success, or -1 on failure |
152 | | * |
153 | | * This function is used to fetch user information for EAP. The user will be |
154 | | * selected based on the specified identity. sm->user and |
155 | | * sm->user_eap_method_index are updated for the new user when a matching user |
156 | | * is found. sm->user can be used to get user information (e.g., password). |
157 | | */ |
158 | | int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, |
159 | | int phase2) |
160 | 0 | { |
161 | 0 | struct eap_user *user; |
162 | |
|
163 | 0 | if (sm == NULL || sm->eapol_cb == NULL || |
164 | 0 | sm->eapol_cb->get_eap_user == NULL) |
165 | 0 | return -1; |
166 | | |
167 | 0 | eap_user_free(sm->user); |
168 | 0 | sm->user = NULL; |
169 | |
|
170 | 0 | user = os_zalloc(sizeof(*user)); |
171 | 0 | if (user == NULL) |
172 | 0 | return -1; |
173 | | |
174 | 0 | if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, |
175 | 0 | identity_len, phase2, user) != 0) { |
176 | 0 | eap_user_free(user); |
177 | 0 | return -1; |
178 | 0 | } |
179 | | |
180 | 0 | sm->user = user; |
181 | 0 | sm->user_eap_method_index = 0; |
182 | |
|
183 | 0 | return 0; |
184 | 0 | } |
185 | | |
186 | | |
187 | | void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) |
188 | 0 | { |
189 | 0 | va_list ap; |
190 | 0 | char *buf; |
191 | 0 | int buflen; |
192 | |
|
193 | 0 | if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL) |
194 | 0 | return; |
195 | | |
196 | 0 | va_start(ap, fmt); |
197 | 0 | buflen = vsnprintf(NULL, 0, fmt, ap) + 1; |
198 | 0 | va_end(ap); |
199 | |
|
200 | 0 | buf = os_malloc(buflen); |
201 | 0 | if (buf == NULL) |
202 | 0 | return; |
203 | 0 | va_start(ap, fmt); |
204 | 0 | vsnprintf(buf, buflen, fmt, ap); |
205 | 0 | va_end(ap); |
206 | |
|
207 | 0 | sm->eapol_cb->log_msg(sm->eapol_ctx, buf); |
208 | |
|
209 | 0 | os_free(buf); |
210 | 0 | } |
211 | | |
212 | | |
213 | | SM_STATE(EAP, DISABLED) |
214 | 0 | { |
215 | 0 | SM_ENTRY(EAP, DISABLED); |
216 | 0 | sm->num_rounds = 0; |
217 | 0 | sm->num_rounds_short = 0; |
218 | 0 | } |
219 | | |
220 | | |
221 | | SM_STATE(EAP, INITIALIZE) |
222 | 0 | { |
223 | 0 | SM_ENTRY(EAP, INITIALIZE); |
224 | |
|
225 | 0 | if (sm->eap_if.eapRestart && !sm->cfg->eap_server && sm->identity) { |
226 | | /* |
227 | | * Need to allow internal Identity method to be used instead |
228 | | * of passthrough at the beginning of reauthentication. |
229 | | */ |
230 | 0 | eap_server_clear_identity(sm); |
231 | 0 | } |
232 | |
|
233 | 0 | sm->try_initiate_reauth = false; |
234 | 0 | sm->currentId = -1; |
235 | 0 | sm->eap_if.eapSuccess = false; |
236 | 0 | sm->eap_if.eapFail = false; |
237 | 0 | sm->eap_if.eapTimeout = false; |
238 | 0 | bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
239 | 0 | sm->eap_if.eapKeyData = NULL; |
240 | 0 | sm->eap_if.eapKeyDataLen = 0; |
241 | 0 | os_free(sm->eap_if.eapSessionId); |
242 | 0 | sm->eap_if.eapSessionId = NULL; |
243 | 0 | sm->eap_if.eapSessionIdLen = 0; |
244 | 0 | sm->eap_if.eapKeyAvailable = false; |
245 | 0 | sm->eap_if.eapRestart = false; |
246 | | |
247 | | /* |
248 | | * This is not defined in RFC 4137, but method state needs to be |
249 | | * reseted here so that it does not remain in success state when |
250 | | * re-authentication starts. |
251 | | */ |
252 | 0 | if (sm->m && sm->eap_method_priv) { |
253 | 0 | sm->m->reset(sm, sm->eap_method_priv); |
254 | 0 | sm->eap_method_priv = NULL; |
255 | 0 | } |
256 | 0 | sm->m = NULL; |
257 | 0 | sm->user_eap_method_index = 0; |
258 | |
|
259 | 0 | if (sm->cfg->backend_auth) { |
260 | 0 | sm->currentMethod = EAP_TYPE_NONE; |
261 | | /* parse rxResp, respId, respMethod */ |
262 | 0 | eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); |
263 | 0 | if (sm->rxResp) { |
264 | 0 | sm->currentId = sm->respId; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | sm->num_rounds = 0; |
268 | 0 | sm->num_rounds_short = 0; |
269 | 0 | sm->method_pending = METHOD_PENDING_NONE; |
270 | |
|
271 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED |
272 | 0 | MACSTR, MAC2STR(sm->peer_addr)); |
273 | 0 | } |
274 | | |
275 | | |
276 | | SM_STATE(EAP, PICK_UP_METHOD) |
277 | 0 | { |
278 | 0 | SM_ENTRY(EAP, PICK_UP_METHOD); |
279 | |
|
280 | 0 | if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { |
281 | 0 | sm->currentMethod = sm->respMethod; |
282 | 0 | if (sm->m && sm->eap_method_priv) { |
283 | 0 | sm->m->reset(sm, sm->eap_method_priv); |
284 | 0 | sm->eap_method_priv = NULL; |
285 | 0 | } |
286 | 0 | sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF, |
287 | 0 | sm->currentMethod); |
288 | 0 | if (sm->m && sm->m->initPickUp) { |
289 | 0 | sm->eap_method_priv = sm->m->initPickUp(sm); |
290 | 0 | if (sm->eap_method_priv == NULL) { |
291 | 0 | wpa_printf(MSG_DEBUG, "EAP: Failed to " |
292 | 0 | "initialize EAP method %d", |
293 | 0 | sm->currentMethod); |
294 | 0 | sm->m = NULL; |
295 | 0 | sm->currentMethod = EAP_TYPE_NONE; |
296 | 0 | } |
297 | 0 | } else { |
298 | 0 | sm->m = NULL; |
299 | 0 | sm->currentMethod = EAP_TYPE_NONE; |
300 | 0 | } |
301 | 0 | } |
302 | |
|
303 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD |
304 | 0 | "method=%u", sm->currentMethod); |
305 | 0 | } |
306 | | |
307 | | |
308 | | SM_STATE(EAP, IDLE) |
309 | 0 | { |
310 | 0 | SM_ENTRY(EAP, IDLE); |
311 | |
|
312 | 0 | sm->eap_if.retransWhile = eap_sm_calculateTimeout( |
313 | 0 | sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, |
314 | 0 | sm->methodTimeout); |
315 | 0 | } |
316 | | |
317 | | |
318 | | SM_STATE(EAP, RETRANSMIT) |
319 | 0 | { |
320 | 0 | SM_ENTRY(EAP, RETRANSMIT); |
321 | |
|
322 | 0 | sm->retransCount++; |
323 | 0 | if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { |
324 | 0 | if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) |
325 | 0 | sm->eap_if.eapReq = true; |
326 | 0 | } |
327 | |
|
328 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR, |
329 | 0 | MAC2STR(sm->peer_addr)); |
330 | 0 | } |
331 | | |
332 | | |
333 | | SM_STATE(EAP, RECEIVED) |
334 | 0 | { |
335 | 0 | SM_ENTRY(EAP, RECEIVED); |
336 | | |
337 | | /* parse rxResp, respId, respMethod */ |
338 | 0 | eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); |
339 | 0 | sm->num_rounds++; |
340 | 0 | if (!sm->eap_if.eapRespData || wpabuf_len(sm->eap_if.eapRespData) < 20) |
341 | 0 | sm->num_rounds_short++; |
342 | 0 | else |
343 | 0 | sm->num_rounds_short = 0; |
344 | 0 | } |
345 | | |
346 | | |
347 | | SM_STATE(EAP, DISCARD) |
348 | 0 | { |
349 | 0 | SM_ENTRY(EAP, DISCARD); |
350 | 0 | sm->eap_if.eapResp = false; |
351 | 0 | sm->eap_if.eapNoReq = true; |
352 | 0 | } |
353 | | |
354 | | |
355 | | SM_STATE(EAP, SEND_REQUEST) |
356 | 0 | { |
357 | 0 | SM_ENTRY(EAP, SEND_REQUEST); |
358 | |
|
359 | 0 | sm->retransCount = 0; |
360 | 0 | if (sm->eap_if.eapReqData) { |
361 | 0 | if (wpabuf_len(sm->eap_if.eapReqData) >= 20) |
362 | 0 | sm->num_rounds_short = 0; |
363 | 0 | if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) |
364 | 0 | { |
365 | 0 | sm->eap_if.eapResp = false; |
366 | 0 | sm->eap_if.eapReq = true; |
367 | 0 | } else { |
368 | 0 | sm->eap_if.eapResp = false; |
369 | 0 | sm->eap_if.eapReq = false; |
370 | 0 | } |
371 | 0 | } else { |
372 | 0 | wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); |
373 | 0 | sm->eap_if.eapResp = false; |
374 | 0 | sm->eap_if.eapReq = false; |
375 | 0 | sm->eap_if.eapNoReq = true; |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | |
380 | | SM_STATE(EAP, INTEGRITY_CHECK) |
381 | 0 | { |
382 | 0 | SM_ENTRY(EAP, INTEGRITY_CHECK); |
383 | |
|
384 | 0 | if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { |
385 | 0 | sm->ignore = true; |
386 | 0 | return; |
387 | 0 | } |
388 | | |
389 | 0 | if (sm->m->check) { |
390 | 0 | sm->ignore = sm->m->check(sm, sm->eap_method_priv, |
391 | 0 | sm->eap_if.eapRespData); |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | | |
396 | | SM_STATE(EAP, METHOD_REQUEST) |
397 | 0 | { |
398 | 0 | SM_ENTRY(EAP, METHOD_REQUEST); |
399 | |
|
400 | 0 | if (sm->m == NULL) { |
401 | 0 | wpa_printf(MSG_DEBUG, "EAP: method not initialized"); |
402 | 0 | return; |
403 | 0 | } |
404 | | |
405 | 0 | sm->currentId = eap_sm_nextId(sm, sm->currentId); |
406 | 0 | wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", |
407 | 0 | sm->currentId); |
408 | 0 | sm->lastId = sm->currentId; |
409 | 0 | wpabuf_free(sm->eap_if.eapReqData); |
410 | 0 | sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, |
411 | 0 | sm->currentId); |
412 | 0 | if (sm->m->getTimeout) |
413 | 0 | sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); |
414 | 0 | else |
415 | 0 | sm->methodTimeout = 0; |
416 | 0 | } |
417 | | |
418 | | |
419 | | static void eap_server_erp_init(struct eap_sm *sm) |
420 | 0 | { |
421 | | #ifdef CONFIG_ERP |
422 | | u8 *emsk = NULL; |
423 | | size_t emsk_len = 0; |
424 | | u8 EMSKname[EAP_EMSK_NAME_LEN]; |
425 | | u8 len[2], ctx[3]; |
426 | | const char *domain; |
427 | | size_t domain_len, nai_buf_len; |
428 | | struct eap_server_erp_key *erp = NULL; |
429 | | int pos; |
430 | | |
431 | | domain = eap_get_erp_domain(sm); |
432 | | if (!domain) |
433 | | return; |
434 | | |
435 | | domain_len = os_strlen(domain); |
436 | | |
437 | | nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len; |
438 | | if (nai_buf_len > 253) { |
439 | | /* |
440 | | * keyName-NAI has a maximum length of 253 octet to fit in |
441 | | * RADIUS attributes. |
442 | | */ |
443 | | wpa_printf(MSG_DEBUG, |
444 | | "EAP: Too long realm for ERP keyName-NAI maximum length"); |
445 | | return; |
446 | | } |
447 | | nai_buf_len++; /* null termination */ |
448 | | erp = os_zalloc(sizeof(*erp) + nai_buf_len); |
449 | | if (erp == NULL) |
450 | | goto fail; |
451 | | erp->recv_seq = (u32) -1; |
452 | | |
453 | | emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); |
454 | | if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { |
455 | | wpa_printf(MSG_DEBUG, |
456 | | "EAP: No suitable EMSK available for ERP"); |
457 | | goto fail; |
458 | | } |
459 | | |
460 | | wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); |
461 | | |
462 | | WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); |
463 | | if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen, |
464 | | "EMSK", len, sizeof(len), |
465 | | EMSKname, EAP_EMSK_NAME_LEN) < 0) { |
466 | | wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); |
467 | | goto fail; |
468 | | } |
469 | | wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); |
470 | | |
471 | | pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, |
472 | | EMSKname, EAP_EMSK_NAME_LEN); |
473 | | erp->keyname_nai[pos] = '@'; |
474 | | os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len); |
475 | | |
476 | | WPA_PUT_BE16(len, emsk_len); |
477 | | if (hmac_sha256_kdf(emsk, emsk_len, |
478 | | "EAP Re-authentication Root Key@ietf.org", |
479 | | len, sizeof(len), erp->rRK, emsk_len) < 0) { |
480 | | wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); |
481 | | goto fail; |
482 | | } |
483 | | erp->rRK_len = emsk_len; |
484 | | wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); |
485 | | |
486 | | ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; |
487 | | WPA_PUT_BE16(&ctx[1], erp->rRK_len); |
488 | | if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, |
489 | | "Re-authentication Integrity Key@ietf.org", |
490 | | ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { |
491 | | wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); |
492 | | goto fail; |
493 | | } |
494 | | erp->rIK_len = erp->rRK_len; |
495 | | wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); |
496 | | |
497 | | if (eap_erp_add_key(sm, erp) == 0) { |
498 | | wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", |
499 | | erp->keyname_nai); |
500 | | erp = NULL; |
501 | | } |
502 | | |
503 | | fail: |
504 | | bin_clear_free(emsk, emsk_len); |
505 | | bin_clear_free(erp, sizeof(*erp)); |
506 | | #endif /* CONFIG_ERP */ |
507 | 0 | } |
508 | | |
509 | | |
510 | | SM_STATE(EAP, METHOD_RESPONSE) |
511 | 0 | { |
512 | 0 | SM_ENTRY(EAP, METHOD_RESPONSE); |
513 | |
|
514 | 0 | if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) |
515 | 0 | return; |
516 | | |
517 | 0 | sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); |
518 | 0 | if (sm->m->isDone(sm, sm->eap_method_priv)) { |
519 | 0 | eap_sm_Policy_update(sm, NULL, 0); |
520 | 0 | bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
521 | 0 | if (sm->m->getKey) { |
522 | 0 | sm->eap_if.eapKeyData = sm->m->getKey( |
523 | 0 | sm, sm->eap_method_priv, |
524 | 0 | &sm->eap_if.eapKeyDataLen); |
525 | 0 | } else { |
526 | 0 | sm->eap_if.eapKeyData = NULL; |
527 | 0 | sm->eap_if.eapKeyDataLen = 0; |
528 | 0 | } |
529 | 0 | os_free(sm->eap_if.eapSessionId); |
530 | 0 | sm->eap_if.eapSessionId = NULL; |
531 | 0 | if (sm->m->getSessionId) { |
532 | 0 | sm->eap_if.eapSessionId = sm->m->getSessionId( |
533 | 0 | sm, sm->eap_method_priv, |
534 | 0 | &sm->eap_if.eapSessionIdLen); |
535 | 0 | wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", |
536 | 0 | sm->eap_if.eapSessionId, |
537 | 0 | sm->eap_if.eapSessionIdLen); |
538 | 0 | } |
539 | 0 | if (sm->cfg->erp && sm->m->get_emsk && sm->eap_if.eapSessionId) |
540 | 0 | eap_server_erp_init(sm); |
541 | 0 | sm->methodState = METHOD_END; |
542 | 0 | } else { |
543 | 0 | sm->methodState = METHOD_CONTINUE; |
544 | 0 | } |
545 | 0 | } |
546 | | |
547 | | |
548 | | SM_STATE(EAP, PROPOSE_METHOD) |
549 | 0 | { |
550 | 0 | int vendor; |
551 | 0 | enum eap_type type; |
552 | |
|
553 | 0 | SM_ENTRY(EAP, PROPOSE_METHOD); |
554 | |
|
555 | 0 | sm->try_initiate_reauth = false; |
556 | 0 | try_another_method: |
557 | 0 | type = eap_sm_Policy_getNextMethod(sm, &vendor); |
558 | 0 | if (vendor == EAP_VENDOR_IETF) |
559 | 0 | sm->currentMethod = type; |
560 | 0 | else |
561 | 0 | sm->currentMethod = EAP_TYPE_EXPANDED; |
562 | 0 | if (sm->m && sm->eap_method_priv) { |
563 | 0 | sm->m->reset(sm, sm->eap_method_priv); |
564 | 0 | sm->eap_method_priv = NULL; |
565 | 0 | } |
566 | 0 | sm->m = eap_server_get_eap_method(vendor, type); |
567 | 0 | if (sm->m) { |
568 | 0 | sm->eap_method_priv = sm->m->init(sm); |
569 | 0 | if (sm->eap_method_priv == NULL) { |
570 | 0 | wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " |
571 | 0 | "method %d", sm->currentMethod); |
572 | 0 | sm->m = NULL; |
573 | 0 | sm->currentMethod = EAP_TYPE_NONE; |
574 | 0 | goto try_another_method; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | if (sm->m == NULL) { |
578 | 0 | wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); |
579 | 0 | eap_log_msg(sm, "Could not find suitable EAP method"); |
580 | 0 | sm->decision = DECISION_FAILURE; |
581 | 0 | return; |
582 | 0 | } |
583 | 0 | if (sm->currentMethod == EAP_TYPE_IDENTITY || |
584 | 0 | sm->currentMethod == EAP_TYPE_NOTIFICATION) |
585 | 0 | sm->methodState = METHOD_CONTINUE; |
586 | 0 | else |
587 | 0 | sm->methodState = METHOD_PROPOSED; |
588 | |
|
589 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD |
590 | 0 | "vendor=%u method=%u", vendor, sm->currentMethod); |
591 | 0 | eap_log_msg(sm, "Propose EAP method vendor=%u method=%u", |
592 | 0 | vendor, sm->currentMethod); |
593 | 0 | } |
594 | | |
595 | | |
596 | | SM_STATE(EAP, NAK) |
597 | 0 | { |
598 | 0 | const struct eap_hdr *nak; |
599 | 0 | size_t len = 0; |
600 | 0 | const u8 *pos; |
601 | 0 | const u8 *nak_list = NULL; |
602 | |
|
603 | 0 | SM_ENTRY(EAP, NAK); |
604 | |
|
605 | 0 | if (sm->eap_method_priv) { |
606 | 0 | sm->m->reset(sm, sm->eap_method_priv); |
607 | 0 | sm->eap_method_priv = NULL; |
608 | 0 | } |
609 | 0 | sm->m = NULL; |
610 | |
|
611 | 0 | if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) |
612 | 0 | return; |
613 | | |
614 | 0 | nak = wpabuf_head(sm->eap_if.eapRespData); |
615 | 0 | if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { |
616 | 0 | len = be_to_host16(nak->length); |
617 | 0 | if (len > wpabuf_len(sm->eap_if.eapRespData)) |
618 | 0 | len = wpabuf_len(sm->eap_if.eapRespData); |
619 | 0 | pos = (const u8 *) (nak + 1); |
620 | 0 | len -= sizeof(*nak); |
621 | 0 | if (*pos == EAP_TYPE_NAK) { |
622 | 0 | pos++; |
623 | 0 | len--; |
624 | 0 | nak_list = pos; |
625 | 0 | } |
626 | 0 | } |
627 | 0 | eap_sm_Policy_update(sm, nak_list, len); |
628 | 0 | } |
629 | | |
630 | | |
631 | | SM_STATE(EAP, SELECT_ACTION) |
632 | 0 | { |
633 | 0 | SM_ENTRY(EAP, SELECT_ACTION); |
634 | |
|
635 | 0 | sm->decision = eap_sm_Policy_getDecision(sm); |
636 | 0 | } |
637 | | |
638 | | |
639 | | SM_STATE(EAP, TIMEOUT_FAILURE) |
640 | 0 | { |
641 | 0 | SM_ENTRY(EAP, TIMEOUT_FAILURE); |
642 | |
|
643 | 0 | sm->eap_if.eapTimeout = true; |
644 | |
|
645 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, |
646 | 0 | WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, MAC2STR(sm->peer_addr)); |
647 | 0 | } |
648 | | |
649 | | |
650 | | SM_STATE(EAP, FAILURE) |
651 | 0 | { |
652 | 0 | SM_ENTRY(EAP, FAILURE); |
653 | |
|
654 | 0 | wpabuf_free(sm->eap_if.eapReqData); |
655 | 0 | sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId); |
656 | 0 | wpabuf_free(sm->lastReqData); |
657 | 0 | sm->lastReqData = NULL; |
658 | 0 | sm->eap_if.eapFail = true; |
659 | |
|
660 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE |
661 | 0 | MACSTR, MAC2STR(sm->peer_addr)); |
662 | 0 | } |
663 | | |
664 | | |
665 | | SM_STATE(EAP, SUCCESS) |
666 | 0 | { |
667 | 0 | SM_ENTRY(EAP, SUCCESS); |
668 | |
|
669 | 0 | wpabuf_free(sm->eap_if.eapReqData); |
670 | 0 | sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId); |
671 | 0 | wpabuf_free(sm->lastReqData); |
672 | 0 | sm->lastReqData = NULL; |
673 | 0 | if (sm->eap_if.eapKeyData) |
674 | 0 | sm->eap_if.eapKeyAvailable = true; |
675 | 0 | sm->eap_if.eapSuccess = true; |
676 | |
|
677 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS |
678 | 0 | MACSTR, MAC2STR(sm->peer_addr)); |
679 | 0 | } |
680 | | |
681 | | |
682 | | SM_STATE(EAP, INITIATE_REAUTH_START) |
683 | 0 | { |
684 | 0 | SM_ENTRY(EAP, INITIATE_REAUTH_START); |
685 | |
|
686 | 0 | sm->initiate_reauth_start_sent = true; |
687 | 0 | sm->try_initiate_reauth = true; |
688 | 0 | sm->currentId = eap_sm_nextId(sm, sm->currentId); |
689 | 0 | wpa_printf(MSG_DEBUG, |
690 | 0 | "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d", |
691 | 0 | sm->currentId); |
692 | 0 | sm->lastId = sm->currentId; |
693 | 0 | wpabuf_free(sm->eap_if.eapReqData); |
694 | 0 | sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm, |
695 | 0 | sm->currentId); |
696 | 0 | wpabuf_free(sm->lastReqData); |
697 | 0 | sm->lastReqData = NULL; |
698 | 0 | } |
699 | | |
700 | | |
701 | | #ifdef CONFIG_ERP |
702 | | |
703 | | static void erp_send_finish_reauth(struct eap_sm *sm, |
704 | | struct eap_server_erp_key *erp, u8 id, |
705 | | u8 flags, u16 seq, const char *nai) |
706 | | { |
707 | | size_t plen; |
708 | | struct wpabuf *msg; |
709 | | u8 hash[SHA256_MAC_LEN]; |
710 | | size_t hash_len; |
711 | | u8 seed[4]; |
712 | | |
713 | | if (erp) { |
714 | | switch (erp->cryptosuite) { |
715 | | case EAP_ERP_CS_HMAC_SHA256_256: |
716 | | hash_len = 32; |
717 | | break; |
718 | | case EAP_ERP_CS_HMAC_SHA256_128: |
719 | | hash_len = 16; |
720 | | break; |
721 | | default: |
722 | | return; |
723 | | } |
724 | | } else |
725 | | hash_len = 0; |
726 | | |
727 | | plen = 1 + 2 + 2 + os_strlen(nai); |
728 | | if (hash_len) |
729 | | plen += 1 + hash_len; |
730 | | msg = eap_msg_alloc(EAP_VENDOR_IETF, |
731 | | (enum eap_type) EAP_ERP_TYPE_REAUTH, |
732 | | plen, EAP_CODE_FINISH, id); |
733 | | if (msg == NULL) |
734 | | return; |
735 | | wpabuf_put_u8(msg, flags); |
736 | | wpabuf_put_be16(msg, seq); |
737 | | |
738 | | wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); |
739 | | wpabuf_put_u8(msg, os_strlen(nai)); |
740 | | wpabuf_put_str(msg, nai); |
741 | | |
742 | | if (erp) { |
743 | | wpabuf_put_u8(msg, erp->cryptosuite); |
744 | | if (hmac_sha256(erp->rIK, erp->rIK_len, |
745 | | wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { |
746 | | wpabuf_free(msg); |
747 | | return; |
748 | | } |
749 | | wpabuf_put_data(msg, hash, hash_len); |
750 | | } |
751 | | |
752 | | wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)", |
753 | | flags & 0x80 ? "failure" : "success"); |
754 | | |
755 | | sm->lastId = sm->currentId; |
756 | | sm->currentId = id; |
757 | | wpabuf_free(sm->eap_if.eapReqData); |
758 | | sm->eap_if.eapReqData = msg; |
759 | | wpabuf_free(sm->lastReqData); |
760 | | sm->lastReqData = NULL; |
761 | | |
762 | | if ((flags & 0x80) || !erp) { |
763 | | sm->eap_if.eapFail = true; |
764 | | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE |
765 | | MACSTR, MAC2STR(sm->peer_addr)); |
766 | | return; |
767 | | } |
768 | | |
769 | | bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
770 | | sm->eap_if.eapKeyDataLen = 0; |
771 | | sm->eap_if.eapKeyData = os_malloc(erp->rRK_len); |
772 | | if (!sm->eap_if.eapKeyData) |
773 | | return; |
774 | | |
775 | | WPA_PUT_BE16(seed, seq); |
776 | | WPA_PUT_BE16(&seed[2], erp->rRK_len); |
777 | | if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, |
778 | | "Re-authentication Master Session Key@ietf.org", |
779 | | seed, sizeof(seed), |
780 | | sm->eap_if.eapKeyData, erp->rRK_len) < 0) { |
781 | | wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); |
782 | | bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len); |
783 | | sm->eap_if.eapKeyData = NULL; |
784 | | return; |
785 | | } |
786 | | sm->eap_if.eapKeyDataLen = erp->rRK_len; |
787 | | sm->eap_if.eapKeyAvailable = true; |
788 | | wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", |
789 | | sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
790 | | sm->eap_if.eapSuccess = true; |
791 | | |
792 | | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS |
793 | | MACSTR, MAC2STR(sm->peer_addr)); |
794 | | } |
795 | | |
796 | | |
797 | | SM_STATE(EAP, INITIATE_RECEIVED) |
798 | | { |
799 | | const u8 *pos, *end, *start, *tlvs, *hdr; |
800 | | const struct eap_hdr *ehdr; |
801 | | size_t len; |
802 | | u8 flags; |
803 | | u16 seq; |
804 | | char nai[254]; |
805 | | struct eap_server_erp_key *erp; |
806 | | int max_len; |
807 | | u8 hash[SHA256_MAC_LEN]; |
808 | | size_t hash_len; |
809 | | struct erp_tlvs parse; |
810 | | u8 resp_flags = 0x80; /* default to failure; cleared on success */ |
811 | | |
812 | | SM_ENTRY(EAP, INITIATE_RECEIVED); |
813 | | |
814 | | sm->rxInitiate = false; |
815 | | |
816 | | pos = eap_hdr_validate(EAP_VENDOR_IETF, |
817 | | (enum eap_type) EAP_ERP_TYPE_REAUTH, |
818 | | sm->eap_if.eapRespData, &len); |
819 | | if (pos == NULL) { |
820 | | wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); |
821 | | goto fail; |
822 | | } |
823 | | hdr = wpabuf_head(sm->eap_if.eapRespData); |
824 | | ehdr = wpabuf_head(sm->eap_if.eapRespData); |
825 | | |
826 | | wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len); |
827 | | if (len < 4) { |
828 | | wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth"); |
829 | | goto fail; |
830 | | } |
831 | | end = pos + len; |
832 | | |
833 | | flags = *pos++; |
834 | | seq = WPA_GET_BE16(pos); |
835 | | pos += 2; |
836 | | wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); |
837 | | tlvs = pos; |
838 | | |
839 | | /* |
840 | | * Parse TVs/TLVs. Since we do not yet know the length of the |
841 | | * Authentication Tag, stop parsing if an unknown TV/TLV is seen and |
842 | | * just try to find the keyName-NAI first so that we can check the |
843 | | * Authentication Tag. |
844 | | */ |
845 | | if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0) |
846 | | goto fail; |
847 | | |
848 | | if (!parse.keyname) { |
849 | | wpa_printf(MSG_DEBUG, |
850 | | "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet"); |
851 | | goto fail; |
852 | | } |
853 | | |
854 | | wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI", |
855 | | parse.keyname, parse.keyname_len); |
856 | | if (parse.keyname_len > 253) { |
857 | | wpa_printf(MSG_DEBUG, |
858 | | "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth"); |
859 | | goto fail; |
860 | | } |
861 | | os_memcpy(nai, parse.keyname, parse.keyname_len); |
862 | | nai[parse.keyname_len] = '\0'; |
863 | | |
864 | | if (!sm->cfg->eap_server) { |
865 | | /* |
866 | | * In passthrough case, EAP-Initiate/Re-auth replaces |
867 | | * EAP Identity exchange. Use keyName-NAI as the user identity |
868 | | * and forward EAP-Initiate/Re-auth to the backend |
869 | | * authentication server. |
870 | | */ |
871 | | wpa_printf(MSG_DEBUG, |
872 | | "EAP: Use keyName-NAI as user identity for backend authentication"); |
873 | | eap_server_clear_identity(sm); |
874 | | sm->identity = (u8 *) dup_binstr(parse.keyname, |
875 | | parse.keyname_len); |
876 | | if (!sm->identity) |
877 | | goto fail; |
878 | | sm->identity_len = parse.keyname_len; |
879 | | return; |
880 | | } |
881 | | |
882 | | erp = eap_erp_get_key(sm, nai); |
883 | | if (!erp) { |
884 | | wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", |
885 | | nai); |
886 | | goto report_error; |
887 | | } |
888 | | |
889 | | if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) { |
890 | | wpa_printf(MSG_DEBUG, |
891 | | "EAP: SEQ=%u replayed (already received SEQ=%u)", |
892 | | seq, erp->recv_seq); |
893 | | goto fail; |
894 | | } |
895 | | |
896 | | /* Is there enough room for Cryptosuite and Authentication Tag? */ |
897 | | start = parse.keyname + parse.keyname_len; |
898 | | max_len = end - start; |
899 | | if (max_len < |
900 | | 1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) { |
901 | | wpa_printf(MSG_DEBUG, |
902 | | "EAP: Not enough room for Authentication Tag"); |
903 | | goto fail; |
904 | | } |
905 | | |
906 | | switch (erp->cryptosuite) { |
907 | | case EAP_ERP_CS_HMAC_SHA256_256: |
908 | | if (end[-33] != erp->cryptosuite) { |
909 | | wpa_printf(MSG_DEBUG, |
910 | | "EAP: Different Cryptosuite used"); |
911 | | goto fail; |
912 | | } |
913 | | hash_len = 32; |
914 | | break; |
915 | | case EAP_ERP_CS_HMAC_SHA256_128: |
916 | | if (end[-17] != erp->cryptosuite) { |
917 | | wpa_printf(MSG_DEBUG, |
918 | | "EAP: Different Cryptosuite used"); |
919 | | goto fail; |
920 | | } |
921 | | hash_len = 16; |
922 | | break; |
923 | | default: |
924 | | hash_len = 0; |
925 | | break; |
926 | | } |
927 | | |
928 | | if (hash_len) { |
929 | | if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, |
930 | | end - hdr - hash_len, hash) < 0) |
931 | | goto fail; |
932 | | if (os_memcmp(end - hash_len, hash, hash_len) != 0) { |
933 | | wpa_printf(MSG_DEBUG, |
934 | | "EAP: Authentication Tag mismatch"); |
935 | | goto fail; |
936 | | } |
937 | | } |
938 | | |
939 | | /* Check if any supported CS results in matching tag */ |
940 | | if (!hash_len && max_len >= 1 + 32 && |
941 | | end[-33] == EAP_ERP_CS_HMAC_SHA256_256) { |
942 | | if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, |
943 | | end - hdr - 32, hash) < 0) |
944 | | goto fail; |
945 | | if (os_memcmp(end - 32, hash, 32) == 0) { |
946 | | wpa_printf(MSG_DEBUG, |
947 | | "EAP: Authentication Tag match using HMAC-SHA256-256"); |
948 | | hash_len = 32; |
949 | | erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256; |
950 | | } |
951 | | } |
952 | | |
953 | | if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) { |
954 | | if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, |
955 | | end - hdr - 16, hash) < 0) |
956 | | goto fail; |
957 | | if (os_memcmp(end - 16, hash, 16) == 0) { |
958 | | wpa_printf(MSG_DEBUG, |
959 | | "EAP: Authentication Tag match using HMAC-SHA256-128"); |
960 | | hash_len = 16; |
961 | | erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128; |
962 | | } |
963 | | } |
964 | | |
965 | | if (!hash_len) { |
966 | | wpa_printf(MSG_DEBUG, |
967 | | "EAP: No supported cryptosuite matched Authentication Tag"); |
968 | | goto fail; |
969 | | } |
970 | | end -= 1 + hash_len; |
971 | | |
972 | | /* |
973 | | * Parse TVs/TLVs again now that we know the exact part of the buffer |
974 | | * that contains them. |
975 | | */ |
976 | | wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs", |
977 | | tlvs, end - tlvs); |
978 | | if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0) |
979 | | goto fail; |
980 | | |
981 | | wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u", |
982 | | erp->keyname_nai, seq); |
983 | | erp->recv_seq = seq; |
984 | | resp_flags &= ~0x80; /* R=0 - success */ |
985 | | |
986 | | report_error: |
987 | | erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai); |
988 | | return; |
989 | | |
990 | | fail: |
991 | | sm->ignore = true; |
992 | | } |
993 | | |
994 | | #endif /* CONFIG_ERP */ |
995 | | |
996 | | |
997 | | SM_STATE(EAP, INITIALIZE_PASSTHROUGH) |
998 | 0 | { |
999 | 0 | SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); |
1000 | |
|
1001 | 0 | wpabuf_free(sm->eap_if.aaaEapRespData); |
1002 | 0 | sm->eap_if.aaaEapRespData = NULL; |
1003 | 0 | sm->try_initiate_reauth = false; |
1004 | 0 | } |
1005 | | |
1006 | | |
1007 | | SM_STATE(EAP, IDLE2) |
1008 | 0 | { |
1009 | 0 | SM_ENTRY(EAP, IDLE2); |
1010 | |
|
1011 | 0 | sm->eap_if.retransWhile = eap_sm_calculateTimeout( |
1012 | 0 | sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, |
1013 | 0 | sm->methodTimeout); |
1014 | 0 | } |
1015 | | |
1016 | | |
1017 | | SM_STATE(EAP, RETRANSMIT2) |
1018 | 0 | { |
1019 | 0 | SM_ENTRY(EAP, RETRANSMIT2); |
1020 | |
|
1021 | 0 | sm->retransCount++; |
1022 | 0 | if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { |
1023 | 0 | if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) |
1024 | 0 | sm->eap_if.eapReq = true; |
1025 | 0 | } |
1026 | |
|
1027 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR, |
1028 | 0 | MAC2STR(sm->peer_addr)); |
1029 | 0 | } |
1030 | | |
1031 | | |
1032 | | SM_STATE(EAP, RECEIVED2) |
1033 | 0 | { |
1034 | 0 | SM_ENTRY(EAP, RECEIVED2); |
1035 | | |
1036 | | /* parse rxResp, respId, respMethod */ |
1037 | 0 | eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); |
1038 | 0 | } |
1039 | | |
1040 | | |
1041 | | SM_STATE(EAP, DISCARD2) |
1042 | 0 | { |
1043 | 0 | SM_ENTRY(EAP, DISCARD2); |
1044 | 0 | sm->eap_if.eapResp = false; |
1045 | 0 | sm->eap_if.eapNoReq = true; |
1046 | 0 | } |
1047 | | |
1048 | | |
1049 | | SM_STATE(EAP, SEND_REQUEST2) |
1050 | 0 | { |
1051 | 0 | SM_ENTRY(EAP, SEND_REQUEST2); |
1052 | |
|
1053 | 0 | sm->retransCount = 0; |
1054 | 0 | if (sm->eap_if.eapReqData) { |
1055 | 0 | if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) |
1056 | 0 | { |
1057 | 0 | sm->eap_if.eapResp = false; |
1058 | 0 | sm->eap_if.eapReq = true; |
1059 | 0 | } else { |
1060 | 0 | sm->eap_if.eapResp = false; |
1061 | 0 | sm->eap_if.eapReq = false; |
1062 | 0 | } |
1063 | 0 | } else { |
1064 | 0 | wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData"); |
1065 | 0 | sm->eap_if.eapResp = false; |
1066 | 0 | sm->eap_if.eapReq = false; |
1067 | 0 | sm->eap_if.eapNoReq = true; |
1068 | 0 | } |
1069 | 0 | } |
1070 | | |
1071 | | |
1072 | | SM_STATE(EAP, AAA_REQUEST) |
1073 | 0 | { |
1074 | 0 | SM_ENTRY(EAP, AAA_REQUEST); |
1075 | |
|
1076 | 0 | if (sm->eap_if.eapRespData == NULL) { |
1077 | 0 | wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData"); |
1078 | 0 | return; |
1079 | 0 | } |
1080 | | |
1081 | | /* |
1082 | | * if (respMethod == IDENTITY) |
1083 | | * aaaIdentity = eapRespData |
1084 | | * This is already taken care of by the EAP-Identity method which |
1085 | | * stores the identity into sm->identity. |
1086 | | */ |
1087 | | |
1088 | 0 | eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData); |
1089 | 0 | } |
1090 | | |
1091 | | |
1092 | | SM_STATE(EAP, AAA_RESPONSE) |
1093 | 0 | { |
1094 | 0 | SM_ENTRY(EAP, AAA_RESPONSE); |
1095 | |
|
1096 | 0 | eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); |
1097 | 0 | sm->currentId = eap_sm_getId(sm->eap_if.eapReqData); |
1098 | 0 | sm->methodTimeout = sm->eap_if.aaaMethodTimeout; |
1099 | 0 | } |
1100 | | |
1101 | | |
1102 | | SM_STATE(EAP, AAA_IDLE) |
1103 | 0 | { |
1104 | 0 | SM_ENTRY(EAP, AAA_IDLE); |
1105 | |
|
1106 | 0 | sm->eap_if.aaaFail = false; |
1107 | 0 | sm->eap_if.aaaSuccess = false; |
1108 | 0 | sm->eap_if.aaaEapReq = false; |
1109 | 0 | sm->eap_if.aaaEapNoReq = false; |
1110 | 0 | sm->eap_if.aaaEapResp = true; |
1111 | 0 | } |
1112 | | |
1113 | | |
1114 | | SM_STATE(EAP, TIMEOUT_FAILURE2) |
1115 | 0 | { |
1116 | 0 | SM_ENTRY(EAP, TIMEOUT_FAILURE2); |
1117 | |
|
1118 | 0 | sm->eap_if.eapTimeout = true; |
1119 | |
|
1120 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, |
1121 | 0 | WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, MAC2STR(sm->peer_addr)); |
1122 | 0 | } |
1123 | | |
1124 | | |
1125 | | SM_STATE(EAP, FAILURE2) |
1126 | 0 | { |
1127 | 0 | SM_ENTRY(EAP, FAILURE2); |
1128 | |
|
1129 | 0 | eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); |
1130 | 0 | sm->eap_if.eapFail = true; |
1131 | |
|
1132 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR, |
1133 | 0 | MAC2STR(sm->peer_addr)); |
1134 | 0 | } |
1135 | | |
1136 | | |
1137 | | SM_STATE(EAP, SUCCESS2) |
1138 | 0 | { |
1139 | 0 | SM_ENTRY(EAP, SUCCESS2); |
1140 | |
|
1141 | 0 | eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); |
1142 | |
|
1143 | 0 | sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable; |
1144 | 0 | if (sm->eap_if.aaaEapKeyAvailable) { |
1145 | 0 | EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); |
1146 | 0 | } else { |
1147 | 0 | bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
1148 | 0 | sm->eap_if.eapKeyData = NULL; |
1149 | 0 | sm->eap_if.eapKeyDataLen = 0; |
1150 | 0 | } |
1151 | |
|
1152 | 0 | sm->eap_if.eapSuccess = true; |
1153 | | |
1154 | | /* |
1155 | | * Start reauthentication with identity request even though we know the |
1156 | | * previously used identity. This is needed to get reauthentication |
1157 | | * started properly. |
1158 | | */ |
1159 | 0 | sm->start_reauth = true; |
1160 | |
|
1161 | 0 | wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR, |
1162 | 0 | MAC2STR(sm->peer_addr)); |
1163 | 0 | } |
1164 | | |
1165 | | |
1166 | | SM_STEP(EAP) |
1167 | 0 | { |
1168 | 0 | if (sm->eap_if.eapRestart && sm->eap_if.portEnabled) |
1169 | 0 | SM_ENTER_GLOBAL(EAP, INITIALIZE); |
1170 | 0 | else if (!sm->eap_if.portEnabled) |
1171 | 0 | SM_ENTER_GLOBAL(EAP, DISABLED); |
1172 | 0 | else if (sm->num_rounds > sm->cfg->max_auth_rounds) { |
1173 | 0 | if (sm->num_rounds == sm->cfg->max_auth_rounds + 1) { |
1174 | 0 | wpa_printf(MSG_DEBUG, "EAP: more than %d " |
1175 | 0 | "authentication rounds - abort", |
1176 | 0 | sm->cfg->max_auth_rounds); |
1177 | 0 | sm->num_rounds++; |
1178 | 0 | SM_ENTER_GLOBAL(EAP, FAILURE); |
1179 | 0 | } |
1180 | 0 | } else if (sm->num_rounds_short > sm->cfg->max_auth_rounds_short) { |
1181 | 0 | if (sm->num_rounds_short == |
1182 | 0 | sm->cfg->max_auth_rounds_short + 1) { |
1183 | 0 | wpa_printf(MSG_DEBUG, |
1184 | 0 | "EAP: more than %d authentication rounds (short) - abort", |
1185 | 0 | sm->cfg->max_auth_rounds_short); |
1186 | 0 | sm->num_rounds_short++; |
1187 | 0 | SM_ENTER_GLOBAL(EAP, FAILURE); |
1188 | 0 | } |
1189 | 0 | } else switch (sm->EAP_state) { |
1190 | 0 | case EAP_INITIALIZE: |
1191 | 0 | if (sm->cfg->backend_auth) { |
1192 | 0 | if (!sm->rxResp) |
1193 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1194 | 0 | else if (sm->rxResp && |
1195 | 0 | (sm->respMethod == EAP_TYPE_NAK || |
1196 | 0 | (sm->respMethod == EAP_TYPE_EXPANDED && |
1197 | 0 | sm->respVendor == EAP_VENDOR_IETF && |
1198 | 0 | sm->respVendorMethod == EAP_TYPE_NAK))) |
1199 | 0 | SM_ENTER(EAP, NAK); |
1200 | 0 | else |
1201 | 0 | SM_ENTER(EAP, PICK_UP_METHOD); |
1202 | 0 | } else { |
1203 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1204 | 0 | } |
1205 | 0 | break; |
1206 | 0 | case EAP_PICK_UP_METHOD: |
1207 | 0 | if (sm->currentMethod == EAP_TYPE_NONE) { |
1208 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1209 | 0 | } else { |
1210 | 0 | SM_ENTER(EAP, METHOD_RESPONSE); |
1211 | 0 | } |
1212 | 0 | break; |
1213 | 0 | case EAP_DISABLED: |
1214 | 0 | if (sm->eap_if.portEnabled) |
1215 | 0 | SM_ENTER(EAP, INITIALIZE); |
1216 | 0 | break; |
1217 | 0 | case EAP_IDLE: |
1218 | 0 | if (sm->eap_if.retransWhile == 0) { |
1219 | 0 | if (sm->try_initiate_reauth) { |
1220 | 0 | sm->try_initiate_reauth = false; |
1221 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1222 | 0 | } else { |
1223 | 0 | SM_ENTER(EAP, RETRANSMIT); |
1224 | 0 | } |
1225 | 0 | } else if (sm->eap_if.eapResp) |
1226 | 0 | SM_ENTER(EAP, RECEIVED); |
1227 | 0 | break; |
1228 | 0 | case EAP_RETRANSMIT: |
1229 | 0 | if (sm->retransCount > sm->MaxRetrans) |
1230 | 0 | SM_ENTER(EAP, TIMEOUT_FAILURE); |
1231 | 0 | else |
1232 | 0 | SM_ENTER(EAP, IDLE); |
1233 | 0 | break; |
1234 | 0 | case EAP_RECEIVED: |
1235 | 0 | if (sm->rxResp && (sm->respId == sm->currentId) && |
1236 | 0 | (sm->respMethod == EAP_TYPE_NAK || |
1237 | 0 | (sm->respMethod == EAP_TYPE_EXPANDED && |
1238 | 0 | sm->respVendor == EAP_VENDOR_IETF && |
1239 | 0 | sm->respVendorMethod == EAP_TYPE_NAK)) |
1240 | 0 | && (sm->methodState == METHOD_PROPOSED)) |
1241 | 0 | SM_ENTER(EAP, NAK); |
1242 | 0 | else if (sm->rxResp && (sm->respId == sm->currentId) && |
1243 | 0 | ((sm->respMethod == sm->currentMethod) || |
1244 | 0 | (sm->respMethod == EAP_TYPE_EXPANDED && |
1245 | 0 | sm->respVendor == EAP_VENDOR_IETF && |
1246 | 0 | sm->respVendorMethod == sm->currentMethod))) |
1247 | 0 | SM_ENTER(EAP, INTEGRITY_CHECK); |
1248 | | #ifdef CONFIG_ERP |
1249 | | else if (sm->rxInitiate) |
1250 | | SM_ENTER(EAP, INITIATE_RECEIVED); |
1251 | | #endif /* CONFIG_ERP */ |
1252 | 0 | else { |
1253 | 0 | wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " |
1254 | 0 | "rxResp=%d respId=%d currentId=%d " |
1255 | 0 | "respMethod=%d currentMethod=%d", |
1256 | 0 | sm->rxResp, sm->respId, sm->currentId, |
1257 | 0 | sm->respMethod, sm->currentMethod); |
1258 | 0 | eap_log_msg(sm, "Discard received EAP message"); |
1259 | 0 | SM_ENTER(EAP, DISCARD); |
1260 | 0 | } |
1261 | 0 | break; |
1262 | 0 | case EAP_DISCARD: |
1263 | 0 | SM_ENTER(EAP, IDLE); |
1264 | 0 | break; |
1265 | 0 | case EAP_SEND_REQUEST: |
1266 | 0 | SM_ENTER(EAP, IDLE); |
1267 | 0 | break; |
1268 | 0 | case EAP_INTEGRITY_CHECK: |
1269 | 0 | if (sm->ignore) |
1270 | 0 | SM_ENTER(EAP, DISCARD); |
1271 | 0 | else |
1272 | 0 | SM_ENTER(EAP, METHOD_RESPONSE); |
1273 | 0 | break; |
1274 | 0 | case EAP_METHOD_REQUEST: |
1275 | 0 | if (sm->m == NULL) { |
1276 | | /* |
1277 | | * This transition is not mentioned in RFC 4137, but it |
1278 | | * is needed to handle cleanly a case where EAP method |
1279 | | * initialization fails. |
1280 | | */ |
1281 | 0 | SM_ENTER(EAP, FAILURE); |
1282 | 0 | break; |
1283 | 0 | } |
1284 | 0 | SM_ENTER(EAP, SEND_REQUEST); |
1285 | 0 | if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) { |
1286 | | /* |
1287 | | * This transition is not mentioned in RFC 4137, but it |
1288 | | * is needed to handle cleanly a case where EAP method |
1289 | | * buildReq fails. |
1290 | | */ |
1291 | 0 | wpa_printf(MSG_DEBUG, |
1292 | 0 | "EAP: Method did not return a request"); |
1293 | 0 | SM_ENTER(EAP, FAILURE); |
1294 | 0 | break; |
1295 | 0 | } |
1296 | 0 | break; |
1297 | 0 | case EAP_METHOD_RESPONSE: |
1298 | | /* |
1299 | | * Note: Mechanism to allow EAP methods to wait while going |
1300 | | * through pending processing is an extension to RFC 4137 |
1301 | | * which only defines the transits to SELECT_ACTION and |
1302 | | * METHOD_REQUEST from this METHOD_RESPONSE state. |
1303 | | */ |
1304 | 0 | if (sm->methodState == METHOD_END) |
1305 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1306 | 0 | else if (sm->method_pending == METHOD_PENDING_WAIT) { |
1307 | 0 | wpa_printf(MSG_DEBUG, "EAP: Method has pending " |
1308 | 0 | "processing - wait before proceeding to " |
1309 | 0 | "METHOD_REQUEST state"); |
1310 | 0 | } else if (sm->method_pending == METHOD_PENDING_CONT) { |
1311 | 0 | wpa_printf(MSG_DEBUG, "EAP: Method has completed " |
1312 | 0 | "pending processing - reprocess pending " |
1313 | 0 | "EAP message"); |
1314 | 0 | sm->method_pending = METHOD_PENDING_NONE; |
1315 | 0 | SM_ENTER(EAP, METHOD_RESPONSE); |
1316 | 0 | } else |
1317 | 0 | SM_ENTER(EAP, METHOD_REQUEST); |
1318 | 0 | break; |
1319 | 0 | case EAP_PROPOSE_METHOD: |
1320 | | /* |
1321 | | * Note: Mechanism to allow EAP methods to wait while going |
1322 | | * through pending processing is an extension to RFC 4137 |
1323 | | * which only defines the transit to METHOD_REQUEST from this |
1324 | | * PROPOSE_METHOD state. |
1325 | | */ |
1326 | 0 | if (sm->method_pending == METHOD_PENDING_WAIT) { |
1327 | 0 | wpa_printf(MSG_DEBUG, "EAP: Method has pending " |
1328 | 0 | "processing - wait before proceeding to " |
1329 | 0 | "METHOD_REQUEST state"); |
1330 | 0 | if (sm->user_eap_method_index > 0) |
1331 | 0 | sm->user_eap_method_index--; |
1332 | 0 | } else if (sm->method_pending == METHOD_PENDING_CONT) { |
1333 | 0 | wpa_printf(MSG_DEBUG, "EAP: Method has completed " |
1334 | 0 | "pending processing - reprocess pending " |
1335 | 0 | "EAP message"); |
1336 | 0 | sm->method_pending = METHOD_PENDING_NONE; |
1337 | 0 | SM_ENTER(EAP, PROPOSE_METHOD); |
1338 | 0 | } else |
1339 | 0 | SM_ENTER(EAP, METHOD_REQUEST); |
1340 | 0 | break; |
1341 | 0 | case EAP_NAK: |
1342 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1343 | 0 | break; |
1344 | 0 | case EAP_SELECT_ACTION: |
1345 | 0 | if (sm->decision == DECISION_FAILURE) |
1346 | 0 | SM_ENTER(EAP, FAILURE); |
1347 | 0 | else if (sm->decision == DECISION_SUCCESS) |
1348 | 0 | SM_ENTER(EAP, SUCCESS); |
1349 | 0 | else if (sm->decision == DECISION_PASSTHROUGH) |
1350 | 0 | SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); |
1351 | 0 | else if (sm->decision == DECISION_INITIATE_REAUTH_START) |
1352 | 0 | SM_ENTER(EAP, INITIATE_REAUTH_START); |
1353 | | #ifdef CONFIG_ERP |
1354 | | else if (sm->cfg->eap_server && sm->cfg->erp && sm->rxInitiate) |
1355 | | SM_ENTER(EAP, INITIATE_RECEIVED); |
1356 | | #endif /* CONFIG_ERP */ |
1357 | 0 | else |
1358 | 0 | SM_ENTER(EAP, PROPOSE_METHOD); |
1359 | 0 | break; |
1360 | 0 | case EAP_INITIATE_REAUTH_START: |
1361 | 0 | SM_ENTER(EAP, SEND_REQUEST); |
1362 | 0 | break; |
1363 | 0 | case EAP_INITIATE_RECEIVED: |
1364 | 0 | if (!sm->cfg->eap_server) |
1365 | 0 | SM_ENTER(EAP, SELECT_ACTION); |
1366 | 0 | break; |
1367 | 0 | case EAP_TIMEOUT_FAILURE: |
1368 | 0 | break; |
1369 | 0 | case EAP_FAILURE: |
1370 | 0 | break; |
1371 | 0 | case EAP_SUCCESS: |
1372 | 0 | break; |
1373 | | |
1374 | 0 | case EAP_INITIALIZE_PASSTHROUGH: |
1375 | 0 | if (sm->currentId == -1) |
1376 | 0 | SM_ENTER(EAP, AAA_IDLE); |
1377 | 0 | else |
1378 | 0 | SM_ENTER(EAP, AAA_REQUEST); |
1379 | 0 | break; |
1380 | 0 | case EAP_IDLE2: |
1381 | 0 | if (sm->eap_if.eapResp) |
1382 | 0 | SM_ENTER(EAP, RECEIVED2); |
1383 | 0 | else if (sm->eap_if.retransWhile == 0) |
1384 | 0 | SM_ENTER(EAP, RETRANSMIT2); |
1385 | 0 | break; |
1386 | 0 | case EAP_RETRANSMIT2: |
1387 | 0 | if (sm->retransCount > sm->MaxRetrans) |
1388 | 0 | SM_ENTER(EAP, TIMEOUT_FAILURE2); |
1389 | 0 | else |
1390 | 0 | SM_ENTER(EAP, IDLE2); |
1391 | 0 | break; |
1392 | 0 | case EAP_RECEIVED2: |
1393 | 0 | if (sm->rxResp && (sm->respId == sm->currentId)) |
1394 | 0 | SM_ENTER(EAP, AAA_REQUEST); |
1395 | 0 | else |
1396 | 0 | SM_ENTER(EAP, DISCARD2); |
1397 | 0 | break; |
1398 | 0 | case EAP_DISCARD2: |
1399 | 0 | SM_ENTER(EAP, IDLE2); |
1400 | 0 | break; |
1401 | 0 | case EAP_SEND_REQUEST2: |
1402 | 0 | SM_ENTER(EAP, IDLE2); |
1403 | 0 | break; |
1404 | 0 | case EAP_AAA_REQUEST: |
1405 | 0 | SM_ENTER(EAP, AAA_IDLE); |
1406 | 0 | break; |
1407 | 0 | case EAP_AAA_RESPONSE: |
1408 | 0 | SM_ENTER(EAP, SEND_REQUEST2); |
1409 | 0 | break; |
1410 | 0 | case EAP_AAA_IDLE: |
1411 | 0 | if (sm->eap_if.aaaFail) |
1412 | 0 | SM_ENTER(EAP, FAILURE2); |
1413 | 0 | else if (sm->eap_if.aaaSuccess) |
1414 | 0 | SM_ENTER(EAP, SUCCESS2); |
1415 | 0 | else if (sm->eap_if.aaaEapReq) |
1416 | 0 | SM_ENTER(EAP, AAA_RESPONSE); |
1417 | 0 | else if (sm->eap_if.aaaTimeout) |
1418 | 0 | SM_ENTER(EAP, TIMEOUT_FAILURE2); |
1419 | 0 | break; |
1420 | 0 | case EAP_TIMEOUT_FAILURE2: |
1421 | 0 | break; |
1422 | 0 | case EAP_FAILURE2: |
1423 | 0 | break; |
1424 | 0 | case EAP_SUCCESS2: |
1425 | 0 | break; |
1426 | 0 | } |
1427 | 0 | } |
1428 | | |
1429 | | |
1430 | | static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, |
1431 | | int eapSRTT, int eapRTTVAR, |
1432 | | int methodTimeout) |
1433 | 0 | { |
1434 | 0 | int rto, i; |
1435 | |
|
1436 | 0 | if (sm->try_initiate_reauth) { |
1437 | 0 | wpa_printf(MSG_DEBUG, |
1438 | 0 | "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start"); |
1439 | 0 | return 1; |
1440 | 0 | } |
1441 | | |
1442 | 0 | if (methodTimeout) { |
1443 | | /* |
1444 | | * EAP method (either internal or through AAA server, provided |
1445 | | * timeout hint. Use that as-is as a timeout for retransmitting |
1446 | | * the EAP request if no response is received. |
1447 | | */ |
1448 | 0 | wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " |
1449 | 0 | "(from EAP method hint)", methodTimeout); |
1450 | 0 | return methodTimeout; |
1451 | 0 | } |
1452 | | |
1453 | | /* |
1454 | | * RFC 3748 recommends algorithms described in RFC 2988 for estimation |
1455 | | * of the retransmission timeout. This should be implemented once |
1456 | | * round-trip time measurements are available. For nowm a simple |
1457 | | * backoff mechanism is used instead if there are no EAP method |
1458 | | * specific hints. |
1459 | | * |
1460 | | * SRTT = smoothed round-trip time |
1461 | | * RTTVAR = round-trip time variation |
1462 | | * RTO = retransmission timeout |
1463 | | */ |
1464 | | |
1465 | | /* |
1466 | | * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for |
1467 | | * initial retransmission and then double the RTO to provide back off |
1468 | | * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3 |
1469 | | * modified RTOmax. |
1470 | | */ |
1471 | 0 | rto = 3; |
1472 | 0 | for (i = 0; i < retransCount; i++) { |
1473 | 0 | rto *= 2; |
1474 | 0 | if (rto >= 20) { |
1475 | 0 | rto = 20; |
1476 | 0 | break; |
1477 | 0 | } |
1478 | 0 | } |
1479 | |
|
1480 | 0 | wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " |
1481 | 0 | "(from dynamic back off; retransCount=%d)", |
1482 | 0 | rto, retransCount); |
1483 | |
|
1484 | 0 | return rto; |
1485 | 0 | } |
1486 | | |
1487 | | |
1488 | | static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) |
1489 | 0 | { |
1490 | 0 | const struct eap_hdr *hdr; |
1491 | 0 | size_t plen; |
1492 | | |
1493 | | /* parse rxResp, respId, respMethod */ |
1494 | 0 | sm->rxResp = false; |
1495 | 0 | sm->rxInitiate = false; |
1496 | 0 | sm->respId = -1; |
1497 | 0 | sm->respMethod = EAP_TYPE_NONE; |
1498 | 0 | sm->respVendor = EAP_VENDOR_IETF; |
1499 | 0 | sm->respVendorMethod = EAP_TYPE_NONE; |
1500 | |
|
1501 | 0 | if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) { |
1502 | 0 | wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p " |
1503 | 0 | "len=%lu", resp, |
1504 | 0 | resp ? (unsigned long) wpabuf_len(resp) : 0); |
1505 | 0 | return; |
1506 | 0 | } |
1507 | | |
1508 | 0 | hdr = wpabuf_head(resp); |
1509 | 0 | plen = be_to_host16(hdr->length); |
1510 | 0 | if (plen > wpabuf_len(resp)) { |
1511 | 0 | wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " |
1512 | 0 | "(len=%lu plen=%lu)", |
1513 | 0 | (unsigned long) wpabuf_len(resp), |
1514 | 0 | (unsigned long) plen); |
1515 | 0 | return; |
1516 | 0 | } |
1517 | | |
1518 | 0 | sm->respId = hdr->identifier; |
1519 | |
|
1520 | 0 | if (hdr->code == EAP_CODE_RESPONSE) |
1521 | 0 | sm->rxResp = true; |
1522 | 0 | else if (hdr->code == EAP_CODE_INITIATE) |
1523 | 0 | sm->rxInitiate = true; |
1524 | |
|
1525 | 0 | if (plen > sizeof(*hdr)) { |
1526 | 0 | u8 *pos = (u8 *) (hdr + 1); |
1527 | 0 | sm->respMethod = *pos++; |
1528 | 0 | if (sm->respMethod == EAP_TYPE_EXPANDED) { |
1529 | 0 | if (plen < sizeof(*hdr) + 8) { |
1530 | 0 | wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " |
1531 | 0 | "expanded EAP-Packet (plen=%lu)", |
1532 | 0 | (unsigned long) plen); |
1533 | 0 | return; |
1534 | 0 | } |
1535 | 0 | sm->respVendor = WPA_GET_BE24(pos); |
1536 | 0 | pos += 3; |
1537 | 0 | sm->respVendorMethod = WPA_GET_BE32(pos); |
1538 | 0 | } |
1539 | 0 | } |
1540 | | |
1541 | 0 | wpa_printf(MSG_DEBUG, |
1542 | 0 | "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u", |
1543 | 0 | sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod, |
1544 | 0 | sm->respVendor, sm->respVendorMethod); |
1545 | 0 | } |
1546 | | |
1547 | | |
1548 | | static int eap_sm_getId(const struct wpabuf *data) |
1549 | 0 | { |
1550 | 0 | const struct eap_hdr *hdr; |
1551 | |
|
1552 | 0 | if (data == NULL || wpabuf_len(data) < sizeof(*hdr)) |
1553 | 0 | return -1; |
1554 | | |
1555 | 0 | hdr = wpabuf_head(data); |
1556 | 0 | wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier); |
1557 | 0 | return hdr->identifier; |
1558 | 0 | } |
1559 | | |
1560 | | |
1561 | | static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id) |
1562 | 0 | { |
1563 | 0 | struct wpabuf *msg; |
1564 | 0 | struct eap_hdr *resp; |
1565 | 0 | wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); |
1566 | |
|
1567 | 0 | msg = wpabuf_alloc(sizeof(*resp)); |
1568 | 0 | if (msg == NULL) |
1569 | 0 | return NULL; |
1570 | 0 | resp = wpabuf_put(msg, sizeof(*resp)); |
1571 | 0 | resp->code = EAP_CODE_SUCCESS; |
1572 | 0 | resp->identifier = id; |
1573 | 0 | resp->length = host_to_be16(sizeof(*resp)); |
1574 | |
|
1575 | 0 | return msg; |
1576 | 0 | } |
1577 | | |
1578 | | |
1579 | | static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id) |
1580 | 0 | { |
1581 | 0 | struct wpabuf *msg; |
1582 | 0 | struct eap_hdr *resp; |
1583 | 0 | wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); |
1584 | |
|
1585 | 0 | msg = wpabuf_alloc(sizeof(*resp)); |
1586 | 0 | if (msg == NULL) |
1587 | 0 | return NULL; |
1588 | 0 | resp = wpabuf_put(msg, sizeof(*resp)); |
1589 | 0 | resp->code = EAP_CODE_FAILURE; |
1590 | 0 | resp->identifier = id; |
1591 | 0 | resp->length = host_to_be16(sizeof(*resp)); |
1592 | |
|
1593 | 0 | return msg; |
1594 | 0 | } |
1595 | | |
1596 | | |
1597 | | static int eap_sm_nextId(struct eap_sm *sm, int id) |
1598 | 0 | { |
1599 | 0 | if (id < 0) { |
1600 | | /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a |
1601 | | * random number */ |
1602 | 0 | id = rand() & 0xff; |
1603 | 0 | if (id != sm->lastId) |
1604 | 0 | return id; |
1605 | 0 | } |
1606 | 0 | return (id + 1) & 0xff; |
1607 | 0 | } |
1608 | | |
1609 | | |
1610 | | /** |
1611 | | * eap_sm_process_nak - Process EAP-Response/Nak |
1612 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1613 | | * @nak_list: Nak list (allowed methods) from the supplicant |
1614 | | * @len: Length of nak_list in bytes |
1615 | | * |
1616 | | * This function is called when EAP-Response/Nak is received from the |
1617 | | * supplicant. This can happen for both phase 1 and phase 2 authentications. |
1618 | | */ |
1619 | | void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) |
1620 | 0 | { |
1621 | 0 | int i; |
1622 | 0 | size_t j; |
1623 | |
|
1624 | 0 | if (sm->user == NULL) |
1625 | 0 | return; |
1626 | | |
1627 | 0 | wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " |
1628 | 0 | "index %d)", sm->user_eap_method_index); |
1629 | |
|
1630 | 0 | wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", |
1631 | 0 | (u8 *) sm->user->methods, |
1632 | 0 | EAP_MAX_METHODS * sizeof(sm->user->methods[0])); |
1633 | 0 | wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", |
1634 | 0 | nak_list, len); |
1635 | |
|
1636 | 0 | i = sm->user_eap_method_index; |
1637 | 0 | while (i < EAP_MAX_METHODS && |
1638 | 0 | (sm->user->methods[i].vendor != EAP_VENDOR_IETF || |
1639 | 0 | sm->user->methods[i].method != EAP_TYPE_NONE)) { |
1640 | 0 | if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) |
1641 | 0 | goto not_found; |
1642 | 0 | for (j = 0; j < len; j++) { |
1643 | 0 | if (nak_list[j] == sm->user->methods[i].method) { |
1644 | 0 | break; |
1645 | 0 | } |
1646 | 0 | } |
1647 | |
|
1648 | 0 | if (j < len) { |
1649 | | /* found */ |
1650 | 0 | i++; |
1651 | 0 | continue; |
1652 | 0 | } |
1653 | | |
1654 | 0 | not_found: |
1655 | | /* not found - remove from the list */ |
1656 | 0 | if (i + 1 < EAP_MAX_METHODS) { |
1657 | 0 | os_memmove(&sm->user->methods[i], |
1658 | 0 | &sm->user->methods[i + 1], |
1659 | 0 | (EAP_MAX_METHODS - i - 1) * |
1660 | 0 | sizeof(sm->user->methods[0])); |
1661 | 0 | } |
1662 | 0 | sm->user->methods[EAP_MAX_METHODS - 1].vendor = |
1663 | 0 | EAP_VENDOR_IETF; |
1664 | 0 | sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; |
1665 | 0 | } |
1666 | | |
1667 | 0 | wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", |
1668 | 0 | (u8 *) sm->user->methods, EAP_MAX_METHODS * |
1669 | 0 | sizeof(sm->user->methods[0])); |
1670 | 0 | } |
1671 | | |
1672 | | |
1673 | | static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, |
1674 | | size_t len) |
1675 | 0 | { |
1676 | 0 | if (nak_list == NULL || sm == NULL || sm->user == NULL) |
1677 | 0 | return; |
1678 | | |
1679 | 0 | if (sm->user->phase2) { |
1680 | 0 | wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" |
1681 | 0 | " info was selected - reject"); |
1682 | 0 | sm->decision = DECISION_FAILURE; |
1683 | 0 | return; |
1684 | 0 | } |
1685 | | |
1686 | 0 | eap_sm_process_nak(sm, nak_list, len); |
1687 | 0 | } |
1688 | | |
1689 | | |
1690 | | static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) |
1691 | 0 | { |
1692 | 0 | enum eap_type next; |
1693 | 0 | int idx = sm->user_eap_method_index; |
1694 | | |
1695 | | /* In theory, there should be no problems with starting |
1696 | | * re-authentication with something else than EAP-Request/Identity and |
1697 | | * this does indeed work with wpa_supplicant. However, at least Funk |
1698 | | * Supplicant seemed to ignore re-auth if it skipped |
1699 | | * EAP-Request/Identity. |
1700 | | * Re-auth sets currentId == -1, so that can be used here to select |
1701 | | * whether Identity needs to be requested again. */ |
1702 | 0 | if (sm->identity == NULL || sm->currentId == -1) { |
1703 | 0 | *vendor = EAP_VENDOR_IETF; |
1704 | 0 | next = EAP_TYPE_IDENTITY; |
1705 | 0 | sm->update_user = true; |
1706 | 0 | } else if (sm->user && idx < EAP_MAX_METHODS && |
1707 | 0 | (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || |
1708 | 0 | sm->user->methods[idx].method != EAP_TYPE_NONE)) { |
1709 | 0 | *vendor = sm->user->methods[idx].vendor; |
1710 | 0 | next = sm->user->methods[idx].method; |
1711 | 0 | sm->user_eap_method_index++; |
1712 | 0 | } else { |
1713 | 0 | *vendor = EAP_VENDOR_IETF; |
1714 | 0 | next = EAP_TYPE_NONE; |
1715 | 0 | } |
1716 | 0 | wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", |
1717 | 0 | *vendor, next); |
1718 | 0 | return next; |
1719 | 0 | } |
1720 | | |
1721 | | |
1722 | | static int eap_sm_Policy_getDecision(struct eap_sm *sm) |
1723 | 0 | { |
1724 | 0 | if (!sm->cfg->eap_server && sm->identity && !sm->start_reauth) { |
1725 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); |
1726 | 0 | return DECISION_PASSTHROUGH; |
1727 | 0 | } |
1728 | | |
1729 | 0 | if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && |
1730 | 0 | sm->m->isSuccess(sm, sm->eap_method_priv)) { |
1731 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " |
1732 | 0 | "SUCCESS"); |
1733 | 0 | sm->update_user = true; |
1734 | 0 | return DECISION_SUCCESS; |
1735 | 0 | } |
1736 | | |
1737 | 0 | if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && |
1738 | 0 | !sm->m->isSuccess(sm, sm->eap_method_priv)) { |
1739 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " |
1740 | 0 | "FAILURE"); |
1741 | 0 | sm->update_user = true; |
1742 | 0 | return DECISION_FAILURE; |
1743 | 0 | } |
1744 | | |
1745 | 0 | if ((sm->user == NULL || sm->update_user) && sm->identity && |
1746 | 0 | !sm->start_reauth) { |
1747 | | /* |
1748 | | * Allow Identity method to be started once to allow identity |
1749 | | * selection hint to be sent from the authentication server, |
1750 | | * but prevent a loop of Identity requests by only allowing |
1751 | | * this to happen once. |
1752 | | */ |
1753 | 0 | int id_req = 0; |
1754 | 0 | if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY && |
1755 | 0 | sm->user->methods[0].vendor == EAP_VENDOR_IETF && |
1756 | 0 | sm->user->methods[0].method == EAP_TYPE_IDENTITY) |
1757 | 0 | id_req = 1; |
1758 | 0 | if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { |
1759 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " |
1760 | 0 | "found from database -> FAILURE"); |
1761 | 0 | return DECISION_FAILURE; |
1762 | 0 | } |
1763 | 0 | if (id_req && sm->user && |
1764 | 0 | sm->user->methods[0].vendor == EAP_VENDOR_IETF && |
1765 | 0 | sm->user->methods[0].method == EAP_TYPE_IDENTITY) { |
1766 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: stop " |
1767 | 0 | "identity request loop -> FAILURE"); |
1768 | 0 | sm->update_user = true; |
1769 | 0 | return DECISION_FAILURE; |
1770 | 0 | } |
1771 | 0 | sm->update_user = false; |
1772 | 0 | } |
1773 | 0 | sm->start_reauth = false; |
1774 | |
|
1775 | 0 | if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && |
1776 | 0 | (sm->user->methods[sm->user_eap_method_index].vendor != |
1777 | 0 | EAP_VENDOR_IETF || |
1778 | 0 | sm->user->methods[sm->user_eap_method_index].method != |
1779 | 0 | EAP_TYPE_NONE)) { |
1780 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " |
1781 | 0 | "available -> CONTINUE"); |
1782 | 0 | return DECISION_CONTINUE; |
1783 | 0 | } |
1784 | | |
1785 | 0 | if (!sm->identity && eap_get_erp_send_reauth_start(sm) && |
1786 | 0 | !sm->initiate_reauth_start_sent) { |
1787 | 0 | wpa_printf(MSG_DEBUG, |
1788 | 0 | "EAP: getDecision: send EAP-Initiate/Re-auth-Start"); |
1789 | 0 | return DECISION_INITIATE_REAUTH_START; |
1790 | 0 | } |
1791 | | |
1792 | 0 | if (sm->identity == NULL || sm->currentId == -1) { |
1793 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " |
1794 | 0 | "yet -> CONTINUE"); |
1795 | 0 | return DECISION_CONTINUE; |
1796 | 0 | } |
1797 | | |
1798 | 0 | wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " |
1799 | 0 | "FAILURE"); |
1800 | 0 | return DECISION_FAILURE; |
1801 | 0 | } |
1802 | | |
1803 | | |
1804 | | static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method) |
1805 | 0 | { |
1806 | 0 | return method == EAP_TYPE_IDENTITY; |
1807 | 0 | } |
1808 | | |
1809 | | |
1810 | | /** |
1811 | | * eap_server_sm_step - Step EAP server state machine |
1812 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1813 | | * Returns: 1 if EAP state was changed or 0 if not |
1814 | | * |
1815 | | * This function advances EAP state machine to a new state to match with the |
1816 | | * current variables. This should be called whenever variables used by the EAP |
1817 | | * state machine have changed. |
1818 | | */ |
1819 | | int eap_server_sm_step(struct eap_sm *sm) |
1820 | 0 | { |
1821 | 0 | int res = 0; |
1822 | 0 | do { |
1823 | 0 | sm->changed = false; |
1824 | 0 | SM_STEP_RUN(EAP); |
1825 | 0 | if (sm->changed) |
1826 | 0 | res = 1; |
1827 | 0 | } while (sm->changed); |
1828 | 0 | return res; |
1829 | 0 | } |
1830 | | |
1831 | | |
1832 | | void eap_user_free(struct eap_user *user) |
1833 | 0 | { |
1834 | 0 | if (user == NULL) |
1835 | 0 | return; |
1836 | 0 | bin_clear_free(user->password, user->password_len); |
1837 | 0 | user->password = NULL; |
1838 | 0 | bin_clear_free(user->salt, user->salt_len); |
1839 | 0 | user->salt = NULL; |
1840 | 0 | os_free(user); |
1841 | 0 | } |
1842 | | |
1843 | | |
1844 | | /** |
1845 | | * eap_server_sm_init - Allocate and initialize EAP server state machine |
1846 | | * @eapol_ctx: Context data to be used with eapol_cb calls |
1847 | | * @eapol_cb: Pointer to EAPOL callback functions |
1848 | | * @conf: EAP configuration |
1849 | | * Returns: Pointer to the allocated EAP state machine or %NULL on failure |
1850 | | * |
1851 | | * This function allocates and initializes an EAP state machine. |
1852 | | */ |
1853 | | struct eap_sm * eap_server_sm_init(void *eapol_ctx, |
1854 | | const struct eapol_callbacks *eapol_cb, |
1855 | | const struct eap_config *conf, |
1856 | | const struct eap_session_data *sess) |
1857 | 0 | { |
1858 | 0 | struct eap_sm *sm; |
1859 | |
|
1860 | 0 | sm = os_zalloc(sizeof(*sm)); |
1861 | 0 | if (sm == NULL) |
1862 | 0 | return NULL; |
1863 | 0 | sm->eapol_ctx = eapol_ctx; |
1864 | 0 | sm->eapol_cb = eapol_cb; |
1865 | 0 | sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ |
1866 | 0 | sm->cfg = conf; |
1867 | 0 | if (sess->assoc_wps_ie) |
1868 | 0 | sm->assoc_wps_ie = wpabuf_dup(sess->assoc_wps_ie); |
1869 | 0 | if (sess->assoc_p2p_ie) |
1870 | 0 | sm->assoc_p2p_ie = wpabuf_dup(sess->assoc_p2p_ie); |
1871 | 0 | if (sess->peer_addr) |
1872 | 0 | os_memcpy(sm->peer_addr, sess->peer_addr, ETH_ALEN); |
1873 | | #ifdef CONFIG_TESTING_OPTIONS |
1874 | | sm->tls_test_flags = sess->tls_test_flags; |
1875 | | #endif /* CONFIG_TESTING_OPTIONS */ |
1876 | |
|
1877 | 0 | wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); |
1878 | |
|
1879 | 0 | return sm; |
1880 | 0 | } |
1881 | | |
1882 | | |
1883 | | /** |
1884 | | * eap_server_sm_deinit - Deinitialize and free an EAP server state machine |
1885 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1886 | | * |
1887 | | * This function deinitializes EAP state machine and frees all allocated |
1888 | | * resources. |
1889 | | */ |
1890 | | void eap_server_sm_deinit(struct eap_sm *sm) |
1891 | 0 | { |
1892 | 0 | if (sm == NULL) |
1893 | 0 | return; |
1894 | 0 | wpa_printf(MSG_DEBUG, "EAP: Server state machine removed"); |
1895 | 0 | if (sm->m && sm->eap_method_priv) |
1896 | 0 | sm->m->reset(sm, sm->eap_method_priv); |
1897 | 0 | wpabuf_free(sm->eap_if.eapReqData); |
1898 | 0 | bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); |
1899 | 0 | os_free(sm->eap_if.eapSessionId); |
1900 | 0 | wpabuf_free(sm->lastReqData); |
1901 | 0 | wpabuf_free(sm->eap_if.eapRespData); |
1902 | 0 | os_free(sm->identity); |
1903 | 0 | os_free(sm->serial_num); |
1904 | 0 | wpabuf_free(sm->eap_if.aaaEapReqData); |
1905 | 0 | wpabuf_free(sm->eap_if.aaaEapRespData); |
1906 | 0 | bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen); |
1907 | 0 | eap_user_free(sm->user); |
1908 | 0 | wpabuf_free(sm->assoc_wps_ie); |
1909 | 0 | wpabuf_free(sm->assoc_p2p_ie); |
1910 | 0 | os_free(sm); |
1911 | 0 | } |
1912 | | |
1913 | | |
1914 | | /** |
1915 | | * eap_sm_notify_cached - Notify EAP state machine of cached PMK |
1916 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1917 | | * |
1918 | | * This function is called when PMKSA caching is used to skip EAP |
1919 | | * authentication. |
1920 | | */ |
1921 | | void eap_sm_notify_cached(struct eap_sm *sm) |
1922 | 0 | { |
1923 | 0 | if (sm == NULL) |
1924 | 0 | return; |
1925 | | |
1926 | 0 | sm->EAP_state = EAP_SUCCESS; |
1927 | 0 | } |
1928 | | |
1929 | | |
1930 | | /** |
1931 | | * eap_sm_pending_cb - EAP state machine callback for a pending EAP request |
1932 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1933 | | * |
1934 | | * This function is called when data for a pending EAP-Request is received. |
1935 | | */ |
1936 | | void eap_sm_pending_cb(struct eap_sm *sm) |
1937 | 0 | { |
1938 | 0 | if (sm == NULL) |
1939 | 0 | return; |
1940 | 0 | wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); |
1941 | 0 | if (sm->method_pending == METHOD_PENDING_WAIT) |
1942 | 0 | sm->method_pending = METHOD_PENDING_CONT; |
1943 | 0 | } |
1944 | | |
1945 | | |
1946 | | /** |
1947 | | * eap_sm_method_pending - Query whether EAP method is waiting for pending data |
1948 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1949 | | * Returns: 1 if method is waiting for pending data or 0 if not |
1950 | | */ |
1951 | | int eap_sm_method_pending(struct eap_sm *sm) |
1952 | 0 | { |
1953 | 0 | if (sm == NULL) |
1954 | 0 | return 0; |
1955 | 0 | return sm->method_pending == METHOD_PENDING_WAIT; |
1956 | 0 | } |
1957 | | |
1958 | | |
1959 | | /** |
1960 | | * eap_get_identity - Get the user identity (from EAP-Response/Identity) |
1961 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1962 | | * @len: Buffer for returning identity length |
1963 | | * Returns: Pointer to the user identity or %NULL if not available |
1964 | | */ |
1965 | | const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) |
1966 | 0 | { |
1967 | 0 | *len = sm->identity_len; |
1968 | 0 | return sm->identity; |
1969 | 0 | } |
1970 | | |
1971 | | |
1972 | | /** |
1973 | | * eap_get_serial_num - Get the serial number of user certificate |
1974 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1975 | | * Returns: Pointer to the serial number or %NULL if not available |
1976 | | */ |
1977 | | const char * eap_get_serial_num(struct eap_sm *sm) |
1978 | 0 | { |
1979 | 0 | return sm->serial_num; |
1980 | 0 | } |
1981 | | |
1982 | | |
1983 | | /** |
1984 | | * eap_get_method - Get the used EAP method |
1985 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1986 | | * Returns: Pointer to the method name or %NULL if not available |
1987 | | */ |
1988 | | const char * eap_get_method(struct eap_sm *sm) |
1989 | 0 | { |
1990 | 0 | if (!sm || !sm->m) |
1991 | 0 | return NULL; |
1992 | 0 | return sm->m->name; |
1993 | 0 | } |
1994 | | |
1995 | | |
1996 | | /** |
1997 | | * eap_get_imsi - Get IMSI of the user |
1998 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
1999 | | * Returns: Pointer to IMSI or %NULL if not available |
2000 | | */ |
2001 | | const char * eap_get_imsi(struct eap_sm *sm) |
2002 | 0 | { |
2003 | 0 | if (!sm || sm->imsi[0] == '\0') |
2004 | 0 | return NULL; |
2005 | 0 | return sm->imsi; |
2006 | 0 | } |
2007 | | |
2008 | | |
2009 | | void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) |
2010 | 0 | { |
2011 | | #ifdef CONFIG_ERP |
2012 | | const struct eap_hdr *hdr; |
2013 | | const u8 *pos, *end; |
2014 | | struct erp_tlvs parse; |
2015 | | |
2016 | | if (len < sizeof(*hdr) + 1) |
2017 | | return; |
2018 | | hdr = (const struct eap_hdr *) eap; |
2019 | | end = eap + len; |
2020 | | pos = (const u8 *) (hdr + 1); |
2021 | | if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH) |
2022 | | return; |
2023 | | pos++; |
2024 | | if (pos + 3 > end) |
2025 | | return; |
2026 | | |
2027 | | /* Skip Flags and SEQ */ |
2028 | | pos += 3; |
2029 | | |
2030 | | if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname) |
2031 | | return; |
2032 | | wpa_hexdump_ascii(MSG_DEBUG, |
2033 | | "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI", |
2034 | | parse.keyname, parse.keyname_len); |
2035 | | os_free(sm->identity); |
2036 | | sm->identity = os_malloc(parse.keyname_len); |
2037 | | if (sm->identity) { |
2038 | | os_memcpy(sm->identity, parse.keyname, parse.keyname_len); |
2039 | | sm->identity_len = parse.keyname_len; |
2040 | | } else { |
2041 | | sm->identity_len = 0; |
2042 | | } |
2043 | | #endif /* CONFIG_ERP */ |
2044 | 0 | } |
2045 | | |
2046 | | |
2047 | | /** |
2048 | | * eap_get_interface - Get pointer to EAP-EAPOL interface data |
2049 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
2050 | | * Returns: Pointer to the EAP-EAPOL interface data |
2051 | | */ |
2052 | | struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) |
2053 | 0 | { |
2054 | 0 | return &sm->eap_if; |
2055 | 0 | } |
2056 | | |
2057 | | |
2058 | | /** |
2059 | | * eap_server_clear_identity - Clear EAP identity information |
2060 | | * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() |
2061 | | * |
2062 | | * This function can be used to clear the EAP identity information in the EAP |
2063 | | * server context. This allows the EAP/Identity method to be used again after |
2064 | | * EAPOL-Start or EAPOL-Logoff. |
2065 | | */ |
2066 | | void eap_server_clear_identity(struct eap_sm *sm) |
2067 | 0 | { |
2068 | 0 | os_free(sm->identity); |
2069 | 0 | sm->identity = NULL; |
2070 | 0 | } |
2071 | | |
2072 | | |
2073 | | #ifdef CONFIG_TESTING_OPTIONS |
2074 | | void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, |
2075 | | const u8 *username, size_t username_len, |
2076 | | const u8 *challenge, const u8 *response) |
2077 | | { |
2078 | | char hex_challenge[30], hex_response[90], user[100]; |
2079 | | |
2080 | | /* Print out Challenge and Response in format supported by asleap. */ |
2081 | | if (username) |
2082 | | printf_encode(user, sizeof(user), username, username_len); |
2083 | | else |
2084 | | user[0] = '\0'; |
2085 | | wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge), |
2086 | | challenge, sizeof(challenge), ':'); |
2087 | | wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24, |
2088 | | ':'); |
2089 | | wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s", |
2090 | | source, user, hex_challenge, hex_response); |
2091 | | } |
2092 | | #endif /* CONFIG_TESTING_OPTIONS */ |
2093 | | |
2094 | | |
2095 | | void eap_server_config_free(struct eap_config *cfg) |
2096 | 0 | { |
2097 | 0 | if (!cfg) |
2098 | 0 | return; |
2099 | 0 | os_free(cfg->pac_opaque_encr_key); |
2100 | 0 | os_free(cfg->eap_fast_a_id); |
2101 | 0 | os_free(cfg->eap_fast_a_id_info); |
2102 | 0 | os_free(cfg->server_id); |
2103 | 0 | os_free(cfg); |
2104 | 0 | } |