Branch data Line data Source code
1 : : /*
2 : : * hostapd / EAP Full Authenticator state machine (RFC 4137)
3 : : * Copyright (c) 2004-2007, 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 "eap_i.h"
19 : : #include "state_machine.h"
20 : : #include "common/wpa_ctrl.h"
21 : :
22 : : #define STATE_MACHINE_DATA struct eap_sm
23 : : #define STATE_MACHINE_DEBUG_PREFIX "EAP"
24 : :
25 : : #define EAP_MAX_AUTH_ROUNDS 50
26 : :
27 : : static void eap_user_free(struct eap_user *user);
28 : :
29 : :
30 : : /* EAP state machines are described in RFC 4137 */
31 : :
32 : : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
33 : : int eapSRTT, int eapRTTVAR,
34 : : int methodTimeout);
35 : : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
36 : : static int eap_sm_getId(const struct wpabuf *data);
37 : : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
38 : : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
39 : : static int eap_sm_nextId(struct eap_sm *sm, int id);
40 : : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
41 : : size_t len);
42 : : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
43 : : static int eap_sm_Policy_getDecision(struct eap_sm *sm);
44 : : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
45 : :
46 : :
47 : 513 : static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
48 : : {
49 [ - + ]: 513 : if (src == NULL)
50 : 0 : return -1;
51 : :
52 : 513 : wpabuf_free(*dst);
53 : 513 : *dst = wpabuf_dup(src);
54 [ + - ]: 513 : return *dst ? 0 : -1;
55 : : }
56 : :
57 : :
58 : 0 : static int eap_copy_data(u8 **dst, size_t *dst_len,
59 : : const u8 *src, size_t src_len)
60 : : {
61 [ # # ]: 0 : if (src == NULL)
62 : 0 : return -1;
63 : :
64 : 0 : os_free(*dst);
65 : 0 : *dst = os_malloc(src_len);
66 [ # # ]: 0 : if (*dst) {
67 : 0 : os_memcpy(*dst, src, src_len);
68 : 0 : *dst_len = src_len;
69 : 0 : return 0;
70 : : } else {
71 : 0 : *dst_len = 0;
72 : 0 : return -1;
73 : : }
74 : : }
75 : :
76 : : #define EAP_COPY(dst, src) \
77 : : eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
78 : :
79 : :
80 : : /**
81 : : * eap_user_get - Fetch user information from the database
82 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
83 : : * @identity: Identity (User-Name) of the user
84 : : * @identity_len: Length of identity in bytes
85 : : * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
86 : : * Returns: 0 on success, or -1 on failure
87 : : *
88 : : * This function is used to fetch user information for EAP. The user will be
89 : : * selected based on the specified identity. sm->user and
90 : : * sm->user_eap_method_index are updated for the new user when a matching user
91 : : * is found. sm->user can be used to get user information (e.g., password).
92 : : */
93 : 203 : int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
94 : : int phase2)
95 : : {
96 : : struct eap_user *user;
97 : :
98 [ + - ][ + - ]: 203 : if (sm == NULL || sm->eapol_cb == NULL ||
[ - + ]
99 : 203 : sm->eapol_cb->get_eap_user == NULL)
100 : 0 : return -1;
101 : :
102 : 203 : eap_user_free(sm->user);
103 : 203 : sm->user = NULL;
104 : :
105 : 203 : user = os_zalloc(sizeof(*user));
106 [ - + ]: 203 : if (user == NULL)
107 : 0 : return -1;
108 : :
109 [ - + ]: 203 : if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
110 : : identity_len, phase2, user) != 0) {
111 : 0 : eap_user_free(user);
112 : 0 : return -1;
113 : : }
114 : :
115 : 203 : sm->user = user;
116 : 203 : sm->user_eap_method_index = 0;
117 : :
118 : 203 : return 0;
119 : : }
120 : :
121 : :
122 : 0 : SM_STATE(EAP, DISABLED)
123 : : {
124 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, DISABLED);
125 : 0 : sm->num_rounds = 0;
126 : 0 : }
127 : :
128 : :
129 : 122 : SM_STATE(EAP, INITIALIZE)
130 : : {
131 [ + - ][ + - ]: 122 : SM_ENTRY(EAP, INITIALIZE);
132 : :
133 [ + - ][ - + ]: 122 : if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
[ # # ]
134 : : /*
135 : : * Need to allow internal Identity method to be used instead
136 : : * of passthrough at the beginning of reauthentication.
137 : : */
138 : 0 : eap_server_clear_identity(sm);
139 : : }
140 : :
141 : 122 : sm->currentId = -1;
142 : 122 : sm->eap_if.eapSuccess = FALSE;
143 : 122 : sm->eap_if.eapFail = FALSE;
144 : 122 : sm->eap_if.eapTimeout = FALSE;
145 : 122 : os_free(sm->eap_if.eapKeyData);
146 : 122 : sm->eap_if.eapKeyData = NULL;
147 : 122 : sm->eap_if.eapKeyDataLen = 0;
148 : 122 : sm->eap_if.eapKeyAvailable = FALSE;
149 : 122 : sm->eap_if.eapRestart = FALSE;
150 : :
151 : : /*
152 : : * This is not defined in RFC 4137, but method state needs to be
153 : : * reseted here so that it does not remain in success state when
154 : : * re-authentication starts.
155 : : */
156 [ - + ][ # # ]: 122 : if (sm->m && sm->eap_method_priv) {
157 : 0 : sm->m->reset(sm, sm->eap_method_priv);
158 : 0 : sm->eap_method_priv = NULL;
159 : : }
160 : 122 : sm->m = NULL;
161 : 122 : sm->user_eap_method_index = 0;
162 : :
163 [ + - ]: 122 : if (sm->backend_auth) {
164 : 122 : sm->currentMethod = EAP_TYPE_NONE;
165 : : /* parse rxResp, respId, respMethod */
166 : 122 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
167 [ + - ]: 122 : if (sm->rxResp) {
168 : 122 : sm->currentId = sm->respId;
169 : : }
170 : : }
171 : 122 : sm->num_rounds = 0;
172 : 122 : sm->method_pending = METHOD_PENDING_NONE;
173 : :
174 : 122 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
175 : 732 : MACSTR, MAC2STR(sm->peer_addr));
176 : 122 : }
177 : :
178 : :
179 : 122 : SM_STATE(EAP, PICK_UP_METHOD)
180 : : {
181 [ - + ][ # # ]: 122 : SM_ENTRY(EAP, PICK_UP_METHOD);
182 : :
183 [ + - ]: 122 : if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
184 : 122 : sm->currentMethod = sm->respMethod;
185 [ - + ][ # # ]: 122 : if (sm->m && sm->eap_method_priv) {
186 : 0 : sm->m->reset(sm, sm->eap_method_priv);
187 : 0 : sm->eap_method_priv = NULL;
188 : : }
189 : 122 : sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
190 : : sm->currentMethod);
191 [ + - ][ + - ]: 122 : if (sm->m && sm->m->initPickUp) {
192 : 122 : sm->eap_method_priv = sm->m->initPickUp(sm);
193 [ - + ]: 122 : if (sm->eap_method_priv == NULL) {
194 : 0 : wpa_printf(MSG_DEBUG, "EAP: Failed to "
195 : : "initialize EAP method %d",
196 : 0 : sm->currentMethod);
197 : 0 : sm->m = NULL;
198 : 0 : sm->currentMethod = EAP_TYPE_NONE;
199 : : }
200 : : } else {
201 : 0 : sm->m = NULL;
202 : 0 : sm->currentMethod = EAP_TYPE_NONE;
203 : : }
204 : : }
205 : :
206 : 122 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
207 : 122 : "method=%u", sm->currentMethod);
208 : 122 : }
209 : :
210 : :
211 : 513 : SM_STATE(EAP, IDLE)
212 : : {
213 [ - + ][ # # ]: 513 : SM_ENTRY(EAP, IDLE);
214 : :
215 : 513 : sm->eap_if.retransWhile = eap_sm_calculateTimeout(
216 : : sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
217 : : sm->methodTimeout);
218 : 513 : }
219 : :
220 : :
221 : 0 : SM_STATE(EAP, RETRANSMIT)
222 : : {
223 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, RETRANSMIT);
224 : :
225 : 0 : sm->retransCount++;
226 [ # # ][ # # ]: 0 : if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
227 [ # # ]: 0 : if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
228 : 0 : sm->eap_if.eapReq = TRUE;
229 : : }
230 : 0 : }
231 : :
232 : :
233 : 511 : SM_STATE(EAP, RECEIVED)
234 : : {
235 [ - + ][ # # ]: 511 : SM_ENTRY(EAP, RECEIVED);
236 : :
237 : : /* parse rxResp, respId, respMethod */
238 : 511 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
239 : 511 : sm->num_rounds++;
240 : 511 : }
241 : :
242 : :
243 : 0 : SM_STATE(EAP, DISCARD)
244 : : {
245 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, DISCARD);
246 : 0 : sm->eap_if.eapResp = FALSE;
247 : 0 : sm->eap_if.eapNoReq = TRUE;
248 : 0 : }
249 : :
250 : :
251 : 513 : SM_STATE(EAP, SEND_REQUEST)
252 : : {
253 [ - + ][ # # ]: 513 : SM_ENTRY(EAP, SEND_REQUEST);
254 : :
255 : 513 : sm->retransCount = 0;
256 [ + - ]: 513 : if (sm->eap_if.eapReqData) {
257 [ + - ]: 513 : if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
258 : : {
259 : 513 : sm->eap_if.eapResp = FALSE;
260 : 513 : sm->eap_if.eapReq = TRUE;
261 : : } else {
262 : 0 : sm->eap_if.eapResp = FALSE;
263 : 0 : sm->eap_if.eapReq = FALSE;
264 : : }
265 : : } else {
266 : 0 : wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
267 : 0 : sm->eap_if.eapResp = FALSE;
268 : 0 : sm->eap_if.eapReq = FALSE;
269 : 0 : sm->eap_if.eapNoReq = TRUE;
270 : : }
271 : 513 : }
272 : :
273 : :
274 : 490 : SM_STATE(EAP, INTEGRITY_CHECK)
275 : : {
276 [ - + ][ # # ]: 490 : SM_ENTRY(EAP, INTEGRITY_CHECK);
277 : :
278 [ - + ]: 490 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
279 : 0 : sm->ignore = TRUE;
280 : 490 : return;
281 : : }
282 : :
283 [ + - ]: 490 : if (sm->m->check) {
284 : 490 : sm->ignore = sm->m->check(sm, sm->eap_method_priv,
285 : : sm->eap_if.eapRespData);
286 : : }
287 : : }
288 : :
289 : :
290 : 513 : SM_STATE(EAP, METHOD_REQUEST)
291 : : {
292 [ - + ][ # # ]: 513 : SM_ENTRY(EAP, METHOD_REQUEST);
293 : :
294 [ - + ]: 513 : if (sm->m == NULL) {
295 : 0 : wpa_printf(MSG_DEBUG, "EAP: method not initialized");
296 : 513 : return;
297 : : }
298 : :
299 : 513 : sm->currentId = eap_sm_nextId(sm, sm->currentId);
300 : 513 : wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
301 : : sm->currentId);
302 : 513 : sm->lastId = sm->currentId;
303 : 513 : wpabuf_free(sm->eap_if.eapReqData);
304 : 513 : sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
305 : 513 : sm->currentId);
306 [ - + ]: 513 : if (sm->m->getTimeout)
307 : 0 : sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
308 : : else
309 : 513 : sm->methodTimeout = 0;
310 : : }
311 : :
312 : :
313 : 624 : SM_STATE(EAP, METHOD_RESPONSE)
314 : : {
315 [ - + ][ # # ]: 624 : SM_ENTRY(EAP, METHOD_RESPONSE);
316 : :
317 [ - + ]: 624 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
318 : 624 : return;
319 : :
320 : 624 : sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
321 [ + + ]: 624 : if (sm->m->isDone(sm, sm->eap_method_priv)) {
322 : 242 : eap_sm_Policy_update(sm, NULL, 0);
323 : 242 : os_free(sm->eap_if.eapKeyData);
324 [ + + ]: 242 : if (sm->m->getKey) {
325 : 120 : sm->eap_if.eapKeyData = sm->m->getKey(
326 : : sm, sm->eap_method_priv,
327 : : &sm->eap_if.eapKeyDataLen);
328 : : } else {
329 : 122 : sm->eap_if.eapKeyData = NULL;
330 : 122 : sm->eap_if.eapKeyDataLen = 0;
331 : : }
332 : 242 : sm->methodState = METHOD_END;
333 : : } else {
334 : 382 : sm->methodState = METHOD_CONTINUE;
335 : : }
336 : : }
337 : :
338 : :
339 : 143 : SM_STATE(EAP, PROPOSE_METHOD)
340 : : {
341 : : int vendor;
342 : : EapType type;
343 : :
344 [ - + ][ # # ]: 143 : SM_ENTRY(EAP, PROPOSE_METHOD);
345 : :
346 : : try_another_method:
347 : 143 : type = eap_sm_Policy_getNextMethod(sm, &vendor);
348 [ + + ]: 143 : if (vendor == EAP_VENDOR_IETF)
349 : 141 : sm->currentMethod = type;
350 : : else
351 : 2 : sm->currentMethod = EAP_TYPE_EXPANDED;
352 [ + + ][ + - ]: 143 : if (sm->m && sm->eap_method_priv) {
353 : 122 : sm->m->reset(sm, sm->eap_method_priv);
354 : 122 : sm->eap_method_priv = NULL;
355 : : }
356 : 143 : sm->m = eap_server_get_eap_method(vendor, type);
357 [ + - ]: 143 : if (sm->m) {
358 : 143 : sm->eap_method_priv = sm->m->init(sm);
359 [ - + ]: 143 : if (sm->eap_method_priv == NULL) {
360 : 0 : wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
361 : 0 : "method %d", sm->currentMethod);
362 : 0 : sm->m = NULL;
363 : 0 : sm->currentMethod = EAP_TYPE_NONE;
364 : 0 : goto try_another_method;
365 : : }
366 : : }
367 [ - + ]: 143 : if (sm->m == NULL) {
368 : 0 : wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
369 : 0 : sm->decision = DECISION_FAILURE;
370 : 143 : return;
371 : : }
372 [ + - ][ - + ]: 143 : if (sm->currentMethod == EAP_TYPE_IDENTITY ||
373 : 143 : sm->currentMethod == EAP_TYPE_NOTIFICATION)
374 : 0 : sm->methodState = METHOD_CONTINUE;
375 : : else
376 : 143 : sm->methodState = METHOD_PROPOSED;
377 : :
378 : 143 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
379 : 143 : "vendor=%u method=%u", vendor, sm->currentMethod);
380 : : }
381 : :
382 : :
383 : 21 : SM_STATE(EAP, NAK)
384 : : {
385 : : const struct eap_hdr *nak;
386 : 21 : size_t len = 0;
387 : : const u8 *pos;
388 : 21 : const u8 *nak_list = NULL;
389 : :
390 [ - + ][ # # ]: 21 : SM_ENTRY(EAP, NAK);
391 : :
392 [ + - ]: 21 : if (sm->eap_method_priv) {
393 : 21 : sm->m->reset(sm, sm->eap_method_priv);
394 : 21 : sm->eap_method_priv = NULL;
395 : : }
396 : 21 : sm->m = NULL;
397 : :
398 [ - + ]: 21 : if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
399 : 21 : return;
400 : :
401 : 21 : nak = wpabuf_head(sm->eap_if.eapRespData);
402 [ + - ][ + - ]: 21 : if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
403 : 21 : len = be_to_host16(nak->length);
404 [ - + ]: 21 : if (len > wpabuf_len(sm->eap_if.eapRespData))
405 : 0 : len = wpabuf_len(sm->eap_if.eapRespData);
406 : 21 : pos = (const u8 *) (nak + 1);
407 : 21 : len -= sizeof(*nak);
408 [ + - ]: 21 : if (*pos == EAP_TYPE_NAK) {
409 : 21 : pos++;
410 : 21 : len--;
411 : 21 : nak_list = pos;
412 : : }
413 : : }
414 : 21 : eap_sm_Policy_update(sm, nak_list, len);
415 : : }
416 : :
417 : :
418 : 263 : SM_STATE(EAP, SELECT_ACTION)
419 : : {
420 [ - + ][ # # ]: 263 : SM_ENTRY(EAP, SELECT_ACTION);
421 : :
422 : 263 : sm->decision = eap_sm_Policy_getDecision(sm);
423 : 263 : }
424 : :
425 : :
426 : 0 : SM_STATE(EAP, TIMEOUT_FAILURE)
427 : : {
428 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, TIMEOUT_FAILURE);
429 : :
430 : 0 : sm->eap_if.eapTimeout = TRUE;
431 : 0 : }
432 : :
433 : :
434 : 15 : SM_STATE(EAP, FAILURE)
435 : : {
436 [ - + ][ # # ]: 15 : SM_ENTRY(EAP, FAILURE);
437 : :
438 : 15 : wpabuf_free(sm->eap_if.eapReqData);
439 : 15 : sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
440 : 15 : wpabuf_free(sm->lastReqData);
441 : 15 : sm->lastReqData = NULL;
442 : 15 : sm->eap_if.eapFail = TRUE;
443 : :
444 : 15 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
445 : 90 : MACSTR, MAC2STR(sm->peer_addr));
446 : 15 : }
447 : :
448 : :
449 : 105 : SM_STATE(EAP, SUCCESS)
450 : : {
451 [ - + ][ # # ]: 105 : SM_ENTRY(EAP, SUCCESS);
452 : :
453 : 105 : wpabuf_free(sm->eap_if.eapReqData);
454 : 105 : sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
455 : 105 : wpabuf_free(sm->lastReqData);
456 : 105 : sm->lastReqData = NULL;
457 [ + - ]: 105 : if (sm->eap_if.eapKeyData)
458 : 105 : sm->eap_if.eapKeyAvailable = TRUE;
459 : 105 : sm->eap_if.eapSuccess = TRUE;
460 : :
461 : 105 : wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
462 : 630 : MACSTR, MAC2STR(sm->peer_addr));
463 : 105 : }
464 : :
465 : :
466 : 0 : SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
467 : : {
468 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
469 : :
470 : 0 : wpabuf_free(sm->eap_if.aaaEapRespData);
471 : 0 : sm->eap_if.aaaEapRespData = NULL;
472 : 0 : }
473 : :
474 : :
475 : 0 : SM_STATE(EAP, IDLE2)
476 : : {
477 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, IDLE2);
478 : :
479 : 0 : sm->eap_if.retransWhile = eap_sm_calculateTimeout(
480 : : sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
481 : : sm->methodTimeout);
482 : 0 : }
483 : :
484 : :
485 : 0 : SM_STATE(EAP, RETRANSMIT2)
486 : : {
487 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, RETRANSMIT2);
488 : :
489 : 0 : sm->retransCount++;
490 [ # # ][ # # ]: 0 : if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
491 [ # # ]: 0 : if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
492 : 0 : sm->eap_if.eapReq = TRUE;
493 : : }
494 : 0 : }
495 : :
496 : :
497 : 0 : SM_STATE(EAP, RECEIVED2)
498 : : {
499 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, RECEIVED2);
500 : :
501 : : /* parse rxResp, respId, respMethod */
502 : 0 : eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
503 : 0 : }
504 : :
505 : :
506 : 0 : SM_STATE(EAP, DISCARD2)
507 : : {
508 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, DISCARD2);
509 : 0 : sm->eap_if.eapResp = FALSE;
510 : 0 : sm->eap_if.eapNoReq = TRUE;
511 : 0 : }
512 : :
513 : :
514 : 0 : SM_STATE(EAP, SEND_REQUEST2)
515 : : {
516 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, SEND_REQUEST2);
517 : :
518 : 0 : sm->retransCount = 0;
519 [ # # ]: 0 : if (sm->eap_if.eapReqData) {
520 [ # # ]: 0 : if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
521 : : {
522 : 0 : sm->eap_if.eapResp = FALSE;
523 : 0 : sm->eap_if.eapReq = TRUE;
524 : : } else {
525 : 0 : sm->eap_if.eapResp = FALSE;
526 : 0 : sm->eap_if.eapReq = FALSE;
527 : : }
528 : : } else {
529 : 0 : wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
530 : 0 : sm->eap_if.eapResp = FALSE;
531 : 0 : sm->eap_if.eapReq = FALSE;
532 : 0 : sm->eap_if.eapNoReq = TRUE;
533 : : }
534 : 0 : }
535 : :
536 : :
537 : 0 : SM_STATE(EAP, AAA_REQUEST)
538 : : {
539 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, AAA_REQUEST);
540 : :
541 [ # # ]: 0 : if (sm->eap_if.eapRespData == NULL) {
542 : 0 : wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
543 : 0 : return;
544 : : }
545 : :
546 : : /*
547 : : * if (respMethod == IDENTITY)
548 : : * aaaIdentity = eapRespData
549 : : * This is already taken care of by the EAP-Identity method which
550 : : * stores the identity into sm->identity.
551 : : */
552 : :
553 : 0 : eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
554 : : }
555 : :
556 : :
557 : 0 : SM_STATE(EAP, AAA_RESPONSE)
558 : : {
559 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, AAA_RESPONSE);
560 : :
561 : 0 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
562 : 0 : sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
563 : 0 : sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
564 : 0 : }
565 : :
566 : :
567 : 0 : SM_STATE(EAP, AAA_IDLE)
568 : : {
569 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, AAA_IDLE);
570 : :
571 : 0 : sm->eap_if.aaaFail = FALSE;
572 : 0 : sm->eap_if.aaaSuccess = FALSE;
573 : 0 : sm->eap_if.aaaEapReq = FALSE;
574 : 0 : sm->eap_if.aaaEapNoReq = FALSE;
575 : 0 : sm->eap_if.aaaEapResp = TRUE;
576 : 0 : }
577 : :
578 : :
579 : 0 : SM_STATE(EAP, TIMEOUT_FAILURE2)
580 : : {
581 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, TIMEOUT_FAILURE2);
582 : :
583 : 0 : sm->eap_if.eapTimeout = TRUE;
584 : 0 : }
585 : :
586 : :
587 : 0 : SM_STATE(EAP, FAILURE2)
588 : : {
589 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, FAILURE2);
590 : :
591 : 0 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
592 : 0 : sm->eap_if.eapFail = TRUE;
593 : 0 : }
594 : :
595 : :
596 : 0 : SM_STATE(EAP, SUCCESS2)
597 : : {
598 [ # # ][ # # ]: 0 : SM_ENTRY(EAP, SUCCESS2);
599 : :
600 : 0 : eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
601 : :
602 : 0 : sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
603 [ # # ]: 0 : if (sm->eap_if.aaaEapKeyAvailable) {
604 : 0 : EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
605 : : } else {
606 : 0 : os_free(sm->eap_if.eapKeyData);
607 : 0 : sm->eap_if.eapKeyData = NULL;
608 : 0 : sm->eap_if.eapKeyDataLen = 0;
609 : : }
610 : :
611 : 0 : sm->eap_if.eapSuccess = TRUE;
612 : :
613 : : /*
614 : : * Start reauthentication with identity request even though we know the
615 : : * previously used identity. This is needed to get reauthentication
616 : : * started properly.
617 : : */
618 : 0 : sm->start_reauth = TRUE;
619 : 0 : }
620 : :
621 : :
622 : 4600 : SM_STEP(EAP)
623 : : {
624 [ + + ][ + - ]: 4600 : if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
625 : 122 : SM_ENTER_GLOBAL(EAP, INITIALIZE);
626 [ - + ]: 4478 : else if (!sm->eap_if.portEnabled)
627 : 0 : SM_ENTER_GLOBAL(EAP, DISABLED);
628 [ - + ]: 4478 : else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
629 [ # # ]: 0 : if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
630 : 0 : wpa_printf(MSG_DEBUG, "EAP: more than %d "
631 : : "authentication rounds - abort",
632 : : EAP_MAX_AUTH_ROUNDS);
633 : 0 : sm->num_rounds++;
634 : 0 : SM_ENTER_GLOBAL(EAP, FAILURE);
635 : : }
636 [ + + - + : 4478 : } else switch (sm->EAP_state) {
- + - + +
+ + + + +
- + + - -
- - - - -
- - - - -
- ]
637 : : case EAP_INITIALIZE:
638 [ + - ]: 122 : if (sm->backend_auth) {
639 [ - + ]: 122 : if (!sm->rxResp)
640 : 0 : SM_ENTER(EAP, SELECT_ACTION);
641 [ + - ][ + - ]: 122 : else if (sm->rxResp &&
642 [ - + ]: 122 : (sm->respMethod == EAP_TYPE_NAK ||
643 [ # # ]: 0 : (sm->respMethod == EAP_TYPE_EXPANDED &&
644 [ # # ]: 0 : sm->respVendor == EAP_VENDOR_IETF &&
645 : 0 : sm->respVendorMethod == EAP_TYPE_NAK)))
646 : 0 : SM_ENTER(EAP, NAK);
647 : : else
648 : 122 : SM_ENTER(EAP, PICK_UP_METHOD);
649 : : } else {
650 : 0 : SM_ENTER(EAP, SELECT_ACTION);
651 : : }
652 : 122 : break;
653 : : case EAP_PICK_UP_METHOD:
654 [ - + ]: 122 : if (sm->currentMethod == EAP_TYPE_NONE) {
655 : 0 : SM_ENTER(EAP, SELECT_ACTION);
656 : : } else {
657 : 122 : SM_ENTER(EAP, METHOD_RESPONSE);
658 : : }
659 : 122 : break;
660 : : case EAP_DISABLED:
661 [ # # ]: 0 : if (sm->eap_if.portEnabled)
662 : 0 : SM_ENTER(EAP, INITIALIZE);
663 : 0 : break;
664 : : case EAP_IDLE:
665 [ - + ]: 1024 : if (sm->eap_if.retransWhile == 0)
666 : 0 : SM_ENTER(EAP, RETRANSMIT);
667 [ + + ]: 1024 : else if (sm->eap_if.eapResp)
668 : 511 : SM_ENTER(EAP, RECEIVED);
669 : 1024 : break;
670 : : case EAP_RETRANSMIT:
671 [ # # ]: 0 : if (sm->retransCount > sm->MaxRetrans)
672 : 0 : SM_ENTER(EAP, TIMEOUT_FAILURE);
673 : : else
674 : 0 : SM_ENTER(EAP, IDLE);
675 : 0 : break;
676 : : case EAP_RECEIVED:
677 [ + - ][ + - ]: 511 : if (sm->rxResp && (sm->respId == sm->currentId) &&
[ + + ]
678 [ + + ]: 490 : (sm->respMethod == EAP_TYPE_NAK ||
679 [ - + ]: 4 : (sm->respMethod == EAP_TYPE_EXPANDED &&
680 [ # # ]: 0 : sm->respVendor == EAP_VENDOR_IETF &&
681 : 0 : sm->respVendorMethod == EAP_TYPE_NAK))
682 [ + - ]: 21 : && (sm->methodState == METHOD_PROPOSED))
683 : 21 : SM_ENTER(EAP, NAK);
684 [ + - ][ + - ]: 490 : else if (sm->rxResp && (sm->respId == sm->currentId) &&
[ - + ]
685 [ # # ]: 0 : ((sm->respMethod == sm->currentMethod) ||
686 [ # # ]: 0 : (sm->respMethod == EAP_TYPE_EXPANDED &&
687 [ # # ]: 0 : sm->respVendor == EAP_VENDOR_IETF &&
688 : 0 : sm->respVendorMethod == sm->currentMethod)))
689 : 490 : SM_ENTER(EAP, INTEGRITY_CHECK);
690 : : else {
691 : 0 : wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
692 : : "rxResp=%d respId=%d currentId=%d "
693 : : "respMethod=%d currentMethod=%d",
694 : 0 : sm->rxResp, sm->respId, sm->currentId,
695 : 0 : sm->respMethod, sm->currentMethod);
696 : 0 : SM_ENTER(EAP, DISCARD);
697 : : }
698 : 511 : break;
699 : : case EAP_DISCARD:
700 : 0 : SM_ENTER(EAP, IDLE);
701 : 0 : break;
702 : : case EAP_SEND_REQUEST:
703 : 513 : SM_ENTER(EAP, IDLE);
704 : 513 : break;
705 : : case EAP_INTEGRITY_CHECK:
706 [ - + ]: 490 : if (sm->ignore)
707 : 0 : SM_ENTER(EAP, DISCARD);
708 : : else
709 : 490 : SM_ENTER(EAP, METHOD_RESPONSE);
710 : 490 : break;
711 : : case EAP_METHOD_REQUEST:
712 [ - + ]: 513 : if (sm->m == NULL) {
713 : : /*
714 : : * This transition is not mentioned in RFC 4137, but it
715 : : * is needed to handle cleanly a case where EAP method
716 : : * initialization fails.
717 : : */
718 : 0 : SM_ENTER(EAP, FAILURE);
719 : 0 : break;
720 : : }
721 : 513 : SM_ENTER(EAP, SEND_REQUEST);
722 : 513 : break;
723 : : case EAP_METHOD_RESPONSE:
724 : : /*
725 : : * Note: Mechanism to allow EAP methods to wait while going
726 : : * through pending processing is an extension to RFC 4137
727 : : * which only defines the transits to SELECT_ACTION and
728 : : * METHOD_REQUEST from this METHOD_RESPONSE state.
729 : : */
730 [ + + ]: 636 : if (sm->methodState == METHOD_END)
731 : 242 : SM_ENTER(EAP, SELECT_ACTION);
732 [ + + ]: 394 : else if (sm->method_pending == METHOD_PENDING_WAIT) {
733 : 12 : wpa_printf(MSG_DEBUG, "EAP: Method has pending "
734 : : "processing - wait before proceeding to "
735 : : "METHOD_REQUEST state");
736 [ + + ]: 382 : } else if (sm->method_pending == METHOD_PENDING_CONT) {
737 : 12 : wpa_printf(MSG_DEBUG, "EAP: Method has completed "
738 : : "pending processing - reprocess pending "
739 : : "EAP message");
740 : 12 : sm->method_pending = METHOD_PENDING_NONE;
741 : 12 : SM_ENTER(EAP, METHOD_RESPONSE);
742 : : } else
743 : 370 : SM_ENTER(EAP, METHOD_REQUEST);
744 : 636 : break;
745 : : case EAP_PROPOSE_METHOD:
746 : : /*
747 : : * Note: Mechanism to allow EAP methods to wait while going
748 : : * through pending processing is an extension to RFC 4137
749 : : * which only defines the transit to METHOD_REQUEST from this
750 : : * PROPOSE_METHOD state.
751 : : */
752 [ - + ]: 143 : if (sm->method_pending == METHOD_PENDING_WAIT) {
753 : 0 : wpa_printf(MSG_DEBUG, "EAP: Method has pending "
754 : : "processing - wait before proceeding to "
755 : : "METHOD_REQUEST state");
756 [ # # ]: 0 : if (sm->user_eap_method_index > 0)
757 : 0 : sm->user_eap_method_index--;
758 [ - + ]: 143 : } else if (sm->method_pending == METHOD_PENDING_CONT) {
759 : 0 : wpa_printf(MSG_DEBUG, "EAP: Method has completed "
760 : : "pending processing - reprocess pending "
761 : : "EAP message");
762 : 0 : sm->method_pending = METHOD_PENDING_NONE;
763 : 0 : SM_ENTER(EAP, PROPOSE_METHOD);
764 : : } else
765 : 143 : SM_ENTER(EAP, METHOD_REQUEST);
766 : 143 : break;
767 : : case EAP_NAK:
768 : 21 : SM_ENTER(EAP, SELECT_ACTION);
769 : 21 : break;
770 : : case EAP_SELECT_ACTION:
771 [ + + ]: 263 : if (sm->decision == DECISION_FAILURE)
772 : 15 : SM_ENTER(EAP, FAILURE);
773 [ + + ]: 248 : else if (sm->decision == DECISION_SUCCESS)
774 : 105 : SM_ENTER(EAP, SUCCESS);
775 [ - + ]: 143 : else if (sm->decision == DECISION_PASSTHROUGH)
776 : 0 : SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
777 : : else
778 : 143 : SM_ENTER(EAP, PROPOSE_METHOD);
779 : 263 : break;
780 : : case EAP_TIMEOUT_FAILURE:
781 : 0 : break;
782 : : case EAP_FAILURE:
783 : 15 : break;
784 : : case EAP_SUCCESS:
785 : 105 : break;
786 : :
787 : : case EAP_INITIALIZE_PASSTHROUGH:
788 [ # # ]: 0 : if (sm->currentId == -1)
789 : 0 : SM_ENTER(EAP, AAA_IDLE);
790 : : else
791 : 0 : SM_ENTER(EAP, AAA_REQUEST);
792 : 0 : break;
793 : : case EAP_IDLE2:
794 [ # # ]: 0 : if (sm->eap_if.eapResp)
795 : 0 : SM_ENTER(EAP, RECEIVED2);
796 [ # # ]: 0 : else if (sm->eap_if.retransWhile == 0)
797 : 0 : SM_ENTER(EAP, RETRANSMIT2);
798 : 0 : break;
799 : : case EAP_RETRANSMIT2:
800 [ # # ]: 0 : if (sm->retransCount > sm->MaxRetrans)
801 : 0 : SM_ENTER(EAP, TIMEOUT_FAILURE2);
802 : : else
803 : 0 : SM_ENTER(EAP, IDLE2);
804 : 0 : break;
805 : : case EAP_RECEIVED2:
806 [ # # ][ # # ]: 0 : if (sm->rxResp && (sm->respId == sm->currentId))
807 : 0 : SM_ENTER(EAP, AAA_REQUEST);
808 : : else
809 : 0 : SM_ENTER(EAP, DISCARD2);
810 : 0 : break;
811 : : case EAP_DISCARD2:
812 : 0 : SM_ENTER(EAP, IDLE2);
813 : 0 : break;
814 : : case EAP_SEND_REQUEST2:
815 : 0 : SM_ENTER(EAP, IDLE2);
816 : 0 : break;
817 : : case EAP_AAA_REQUEST:
818 : 0 : SM_ENTER(EAP, AAA_IDLE);
819 : 0 : break;
820 : : case EAP_AAA_RESPONSE:
821 : 0 : SM_ENTER(EAP, SEND_REQUEST2);
822 : 0 : break;
823 : : case EAP_AAA_IDLE:
824 [ # # ]: 0 : if (sm->eap_if.aaaFail)
825 : 0 : SM_ENTER(EAP, FAILURE2);
826 [ # # ]: 0 : else if (sm->eap_if.aaaSuccess)
827 : 0 : SM_ENTER(EAP, SUCCESS2);
828 [ # # ]: 0 : else if (sm->eap_if.aaaEapReq)
829 : 0 : SM_ENTER(EAP, AAA_RESPONSE);
830 [ # # ]: 0 : else if (sm->eap_if.aaaTimeout)
831 : 0 : SM_ENTER(EAP, TIMEOUT_FAILURE2);
832 : 0 : break;
833 : : case EAP_TIMEOUT_FAILURE2:
834 : 0 : break;
835 : : case EAP_FAILURE2:
836 : 0 : break;
837 : : case EAP_SUCCESS2:
838 : 0 : break;
839 : : }
840 : 4600 : }
841 : :
842 : :
843 : 513 : static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
844 : : int eapSRTT, int eapRTTVAR,
845 : : int methodTimeout)
846 : : {
847 : : int rto, i;
848 : :
849 [ - + ]: 513 : if (methodTimeout) {
850 : : /*
851 : : * EAP method (either internal or through AAA server, provided
852 : : * timeout hint. Use that as-is as a timeout for retransmitting
853 : : * the EAP request if no response is received.
854 : : */
855 : 0 : wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
856 : : "(from EAP method hint)", methodTimeout);
857 : 0 : return methodTimeout;
858 : : }
859 : :
860 : : /*
861 : : * RFC 3748 recommends algorithms described in RFC 2988 for estimation
862 : : * of the retransmission timeout. This should be implemented once
863 : : * round-trip time measurements are available. For nowm a simple
864 : : * backoff mechanism is used instead if there are no EAP method
865 : : * specific hints.
866 : : *
867 : : * SRTT = smoothed round-trip time
868 : : * RTTVAR = round-trip time variation
869 : : * RTO = retransmission timeout
870 : : */
871 : :
872 : : /*
873 : : * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
874 : : * initial retransmission and then double the RTO to provide back off
875 : : * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
876 : : * modified RTOmax.
877 : : */
878 : 513 : rto = 3;
879 [ - + ]: 513 : for (i = 0; i < retransCount; i++) {
880 : 0 : rto *= 2;
881 [ # # ]: 0 : if (rto >= 20) {
882 : 0 : rto = 20;
883 : 0 : break;
884 : : }
885 : : }
886 : :
887 : 513 : wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
888 : : "(from dynamic back off; retransCount=%d)",
889 : : rto, retransCount);
890 : :
891 : 513 : return rto;
892 : : }
893 : :
894 : :
895 : 633 : static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
896 : : {
897 : : const struct eap_hdr *hdr;
898 : : size_t plen;
899 : :
900 : : /* parse rxResp, respId, respMethod */
901 : 633 : sm->rxResp = FALSE;
902 : 633 : sm->respId = -1;
903 : 633 : sm->respMethod = EAP_TYPE_NONE;
904 : 633 : sm->respVendor = EAP_VENDOR_IETF;
905 : 633 : sm->respVendorMethod = EAP_TYPE_NONE;
906 : :
907 [ + - ][ - + ]: 633 : if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
908 [ # # ]: 0 : wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
909 : : "len=%lu", resp,
910 : : resp ? (unsigned long) wpabuf_len(resp) : 0);
911 : 0 : return;
912 : : }
913 : :
914 : 633 : hdr = wpabuf_head(resp);
915 : 633 : plen = be_to_host16(hdr->length);
916 [ - + ]: 633 : if (plen > wpabuf_len(resp)) {
917 : 0 : wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
918 : : "(len=%lu plen=%lu)",
919 : : (unsigned long) wpabuf_len(resp),
920 : : (unsigned long) plen);
921 : 0 : return;
922 : : }
923 : :
924 : 633 : sm->respId = hdr->identifier;
925 : :
926 [ + - ]: 633 : if (hdr->code == EAP_CODE_RESPONSE)
927 : 633 : sm->rxResp = TRUE;
928 : :
929 [ + - ]: 633 : if (plen > sizeof(*hdr)) {
930 : 633 : u8 *pos = (u8 *) (hdr + 1);
931 : 633 : sm->respMethod = *pos++;
932 [ + + ]: 633 : if (sm->respMethod == EAP_TYPE_EXPANDED) {
933 [ - + ]: 4 : if (plen < sizeof(*hdr) + 8) {
934 : 0 : wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
935 : : "expanded EAP-Packet (plen=%lu)",
936 : : (unsigned long) plen);
937 : 0 : return;
938 : : }
939 : 4 : sm->respVendor = WPA_GET_BE24(pos);
940 : 4 : pos += 3;
941 : 4 : sm->respVendorMethod = WPA_GET_BE32(pos);
942 : : }
943 : : }
944 : :
945 : 633 : wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
946 : : "respMethod=%u respVendor=%u respVendorMethod=%u",
947 : 1266 : sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
948 : : sm->respVendorMethod);
949 : : }
950 : :
951 : :
952 : 0 : static int eap_sm_getId(const struct wpabuf *data)
953 : : {
954 : : const struct eap_hdr *hdr;
955 : :
956 [ # # ][ # # ]: 0 : if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
957 : 0 : return -1;
958 : :
959 : 0 : hdr = wpabuf_head(data);
960 : 0 : wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
961 : 0 : return hdr->identifier;
962 : : }
963 : :
964 : :
965 : 105 : static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
966 : : {
967 : : struct wpabuf *msg;
968 : : struct eap_hdr *resp;
969 : 105 : wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
970 : :
971 : 105 : msg = wpabuf_alloc(sizeof(*resp));
972 [ - + ]: 105 : if (msg == NULL)
973 : 0 : return NULL;
974 : 105 : resp = wpabuf_put(msg, sizeof(*resp));
975 : 105 : resp->code = EAP_CODE_SUCCESS;
976 : 105 : resp->identifier = id;
977 : 105 : resp->length = host_to_be16(sizeof(*resp));
978 : :
979 : 105 : return msg;
980 : : }
981 : :
982 : :
983 : 15 : static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
984 : : {
985 : : struct wpabuf *msg;
986 : : struct eap_hdr *resp;
987 : 15 : wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
988 : :
989 : 15 : msg = wpabuf_alloc(sizeof(*resp));
990 [ - + ]: 15 : if (msg == NULL)
991 : 0 : return NULL;
992 : 15 : resp = wpabuf_put(msg, sizeof(*resp));
993 : 15 : resp->code = EAP_CODE_FAILURE;
994 : 15 : resp->identifier = id;
995 : 15 : resp->length = host_to_be16(sizeof(*resp));
996 : :
997 : 15 : return msg;
998 : : }
999 : :
1000 : :
1001 : 513 : static int eap_sm_nextId(struct eap_sm *sm, int id)
1002 : : {
1003 [ - + ]: 513 : if (id < 0) {
1004 : : /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
1005 : : * random number */
1006 : 0 : id = rand() & 0xff;
1007 [ # # ]: 0 : if (id != sm->lastId)
1008 : 0 : return id;
1009 : : }
1010 : 513 : return (id + 1) & 0xff;
1011 : : }
1012 : :
1013 : :
1014 : : /**
1015 : : * eap_sm_process_nak - Process EAP-Response/Nak
1016 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1017 : : * @nak_list: Nak list (allowed methods) from the supplicant
1018 : : * @len: Length of nak_list in bytes
1019 : : *
1020 : : * This function is called when EAP-Response/Nak is received from the
1021 : : * supplicant. This can happen for both phase 1 and phase 2 authentications.
1022 : : */
1023 : 29 : void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
1024 : : {
1025 : : int i;
1026 : : size_t j;
1027 : :
1028 [ - + ]: 29 : if (sm->user == NULL)
1029 : 29 : return;
1030 : :
1031 : 29 : wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
1032 : : "index %d)", sm->user_eap_method_index);
1033 : :
1034 : 29 : wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
1035 : 29 : (u8 *) sm->user->methods,
1036 : : EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
1037 : 29 : wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
1038 : : nak_list, len);
1039 : :
1040 : 29 : i = sm->user_eap_method_index;
1041 [ + - ][ - + ]: 171 : while (i < EAP_MAX_METHODS &&
1042 [ + + ]: 171 : (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
1043 : 171 : sm->user->methods[i].method != EAP_TYPE_NONE)) {
1044 [ - + ]: 142 : if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
1045 : 0 : goto not_found;
1046 [ + + ]: 255 : for (j = 0; j < len; j++) {
1047 [ + + ]: 142 : if (nak_list[j] == sm->user->methods[i].method) {
1048 : 29 : break;
1049 : : }
1050 : : }
1051 : :
1052 [ + + ]: 142 : if (j < len) {
1053 : : /* found */
1054 : 29 : i++;
1055 : 29 : continue;
1056 : : }
1057 : :
1058 : : not_found:
1059 : : /* not found - remove from the list */
1060 [ + - ]: 113 : if (i + 1 < EAP_MAX_METHODS) {
1061 : 113 : os_memmove(&sm->user->methods[i],
1062 : : &sm->user->methods[i + 1],
1063 : : (EAP_MAX_METHODS - i - 1) *
1064 : : sizeof(sm->user->methods[0]));
1065 : : }
1066 : 113 : sm->user->methods[EAP_MAX_METHODS - 1].vendor =
1067 : : EAP_VENDOR_IETF;
1068 : 113 : sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
1069 : : }
1070 : :
1071 : 29 : wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
1072 : 29 : (u8 *) sm->user->methods, EAP_MAX_METHODS *
1073 : : sizeof(sm->user->methods[0]));
1074 : : }
1075 : :
1076 : :
1077 : 263 : static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
1078 : : size_t len)
1079 : : {
1080 [ + + ][ + - ]: 263 : if (nak_list == NULL || sm == NULL || sm->user == NULL)
[ - + ]
1081 : 242 : return;
1082 : :
1083 [ - + ]: 21 : if (sm->user->phase2) {
1084 : 0 : wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
1085 : : " info was selected - reject");
1086 : 0 : sm->decision = DECISION_FAILURE;
1087 : 0 : return;
1088 : : }
1089 : :
1090 : 263 : eap_sm_process_nak(sm, nak_list, len);
1091 : : }
1092 : :
1093 : :
1094 : 143 : static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
1095 : : {
1096 : : EapType next;
1097 : 143 : int idx = sm->user_eap_method_index;
1098 : :
1099 : : /* In theory, there should be no problems with starting
1100 : : * re-authentication with something else than EAP-Request/Identity and
1101 : : * this does indeed work with wpa_supplicant. However, at least Funk
1102 : : * Supplicant seemed to ignore re-auth if it skipped
1103 : : * EAP-Request/Identity.
1104 : : * Re-auth sets currentId == -1, so that can be used here to select
1105 : : * whether Identity needs to be requested again. */
1106 [ + - ][ - + ]: 143 : if (sm->identity == NULL || sm->currentId == -1) {
1107 : 0 : *vendor = EAP_VENDOR_IETF;
1108 : 0 : next = EAP_TYPE_IDENTITY;
1109 : 0 : sm->update_user = TRUE;
1110 [ + - ][ + - ]: 143 : } else if (sm->user && idx < EAP_MAX_METHODS &&
[ + + ]
1111 [ + - ]: 141 : (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
1112 : 141 : sm->user->methods[idx].method != EAP_TYPE_NONE)) {
1113 : 143 : *vendor = sm->user->methods[idx].vendor;
1114 : 143 : next = sm->user->methods[idx].method;
1115 : 143 : sm->user_eap_method_index++;
1116 : : } else {
1117 : 0 : *vendor = EAP_VENDOR_IETF;
1118 : 0 : next = EAP_TYPE_NONE;
1119 : : }
1120 : 143 : wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
1121 : : *vendor, next);
1122 : 143 : return next;
1123 : : }
1124 : :
1125 : :
1126 : 263 : static int eap_sm_Policy_getDecision(struct eap_sm *sm)
1127 : : {
1128 [ - + ][ # # ]: 263 : if (!sm->eap_server && sm->identity && !sm->start_reauth) {
[ # # ]
1129 : 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
1130 : 0 : return DECISION_PASSTHROUGH;
1131 : : }
1132 : :
1133 [ + + ]: 383 : if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
[ + + + + ]
1134 : 120 : sm->m->isSuccess(sm, sm->eap_method_priv)) {
1135 : 105 : wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
1136 : : "SUCCESS");
1137 : 105 : sm->update_user = TRUE;
1138 : 105 : return DECISION_SUCCESS;
1139 : : }
1140 : :
1141 [ + + ]: 295 : if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
[ + - + + ]
1142 : 137 : !sm->m->isSuccess(sm, sm->eap_method_priv)) {
1143 : 15 : wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
1144 : : "FAILURE");
1145 : 15 : sm->update_user = TRUE;
1146 : 15 : return DECISION_FAILURE;
1147 : : }
1148 : :
1149 [ + + ][ - + ]: 143 : if ((sm->user == NULL || sm->update_user) && sm->identity &&
[ + - ][ + - ]
1150 : 122 : !sm->start_reauth) {
1151 : : /*
1152 : : * Allow Identity method to be started once to allow identity
1153 : : * selection hint to be sent from the authentication server,
1154 : : * but prevent a loop of Identity requests by only allowing
1155 : : * this to happen once.
1156 : : */
1157 : 122 : int id_req = 0;
1158 [ - + ][ # # ]: 122 : if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
[ # # ]
1159 [ # # ]: 0 : sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
1160 : 0 : sm->user->methods[0].method == EAP_TYPE_IDENTITY)
1161 : 0 : id_req = 1;
1162 [ - + ]: 122 : if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
1163 : 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
1164 : : "found from database -> FAILURE");
1165 : 0 : return DECISION_FAILURE;
1166 : : }
1167 [ - + ][ # # ]: 122 : if (id_req && sm->user &&
[ # # ]
1168 [ # # ]: 0 : sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
1169 : 0 : sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
1170 : 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
1171 : : "identity request loop -> FAILURE");
1172 : 0 : sm->update_user = TRUE;
1173 : 0 : return DECISION_FAILURE;
1174 : : }
1175 : 122 : sm->update_user = FALSE;
1176 : : }
1177 : 143 : sm->start_reauth = FALSE;
1178 : :
1179 [ + - ][ + - ]: 143 : if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
[ + + ]
1180 : 143 : (sm->user->methods[sm->user_eap_method_index].vendor !=
1181 [ + - ]: 141 : EAP_VENDOR_IETF ||
1182 : 141 : sm->user->methods[sm->user_eap_method_index].method !=
1183 : : EAP_TYPE_NONE)) {
1184 : 143 : wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
1185 : : "available -> CONTINUE");
1186 : 143 : return DECISION_CONTINUE;
1187 : : }
1188 : :
1189 [ # # ][ # # ]: 0 : if (sm->identity == NULL || sm->currentId == -1) {
1190 : 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
1191 : : "yet -> CONTINUE");
1192 : 0 : return DECISION_CONTINUE;
1193 : : }
1194 : :
1195 : 0 : wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
1196 : : "FAILURE");
1197 : 263 : return DECISION_FAILURE;
1198 : : }
1199 : :
1200 : :
1201 : 122 : static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
1202 : : {
1203 : 122 : return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
1204 : : }
1205 : :
1206 : :
1207 : : /**
1208 : : * eap_server_sm_step - Step EAP server state machine
1209 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1210 : : * Returns: 1 if EAP state was changed or 0 if not
1211 : : *
1212 : : * This function advances EAP state machine to a new state to match with the
1213 : : * current variables. This should be called whenever variables used by the EAP
1214 : : * state machine have changed.
1215 : : */
1216 : 645 : int eap_server_sm_step(struct eap_sm *sm)
1217 : : {
1218 : 645 : int res = 0;
1219 : : do {
1220 : 4600 : sm->changed = FALSE;
1221 : 4600 : SM_STEP_RUN(EAP);
1222 [ + + ]: 4600 : if (sm->changed)
1223 : 3955 : res = 1;
1224 [ + + ]: 4600 : } while (sm->changed);
1225 : 645 : return res;
1226 : : }
1227 : :
1228 : :
1229 : 325 : static void eap_user_free(struct eap_user *user)
1230 : : {
1231 [ + + ]: 325 : if (user == NULL)
1232 : 325 : return;
1233 : 203 : os_free(user->password);
1234 : 203 : user->password = NULL;
1235 : 203 : os_free(user);
1236 : : }
1237 : :
1238 : :
1239 : : /**
1240 : : * eap_server_sm_init - Allocate and initialize EAP server state machine
1241 : : * @eapol_ctx: Context data to be used with eapol_cb calls
1242 : : * @eapol_cb: Pointer to EAPOL callback functions
1243 : : * @conf: EAP configuration
1244 : : * Returns: Pointer to the allocated EAP state machine or %NULL on failure
1245 : : *
1246 : : * This function allocates and initializes an EAP state machine.
1247 : : */
1248 : 122 : struct eap_sm * eap_server_sm_init(void *eapol_ctx,
1249 : : struct eapol_callbacks *eapol_cb,
1250 : : struct eap_config *conf)
1251 : : {
1252 : : struct eap_sm *sm;
1253 : :
1254 : 122 : sm = os_zalloc(sizeof(*sm));
1255 [ - + ]: 122 : if (sm == NULL)
1256 : 0 : return NULL;
1257 : 122 : sm->eapol_ctx = eapol_ctx;
1258 : 122 : sm->eapol_cb = eapol_cb;
1259 : 122 : sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
1260 : 122 : sm->ssl_ctx = conf->ssl_ctx;
1261 : 122 : sm->msg_ctx = conf->msg_ctx;
1262 : 122 : sm->eap_sim_db_priv = conf->eap_sim_db_priv;
1263 : 122 : sm->backend_auth = conf->backend_auth;
1264 : 122 : sm->eap_server = conf->eap_server;
1265 [ + - ]: 122 : if (conf->pac_opaque_encr_key) {
1266 : 122 : sm->pac_opaque_encr_key = os_malloc(16);
1267 [ + - ]: 122 : if (sm->pac_opaque_encr_key) {
1268 : 122 : os_memcpy(sm->pac_opaque_encr_key,
1269 : : conf->pac_opaque_encr_key, 16);
1270 : : }
1271 : : }
1272 [ + - ]: 122 : if (conf->eap_fast_a_id) {
1273 : 122 : sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
1274 [ + - ]: 122 : if (sm->eap_fast_a_id) {
1275 : 122 : os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
1276 : : conf->eap_fast_a_id_len);
1277 : 122 : sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
1278 : : }
1279 : : }
1280 [ + - ]: 122 : if (conf->eap_fast_a_id_info)
1281 : 122 : sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
1282 : 122 : sm->eap_fast_prov = conf->eap_fast_prov;
1283 : 122 : sm->pac_key_lifetime = conf->pac_key_lifetime;
1284 : 122 : sm->pac_key_refresh_time = conf->pac_key_refresh_time;
1285 : 122 : sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
1286 : 122 : sm->tnc = conf->tnc;
1287 : 122 : sm->wps = conf->wps;
1288 [ - + ]: 122 : if (conf->assoc_wps_ie)
1289 : 0 : sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
1290 [ - + ]: 122 : if (conf->assoc_p2p_ie)
1291 : 0 : sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
1292 [ - + ]: 122 : if (conf->peer_addr)
1293 : 0 : os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
1294 : 122 : sm->fragment_size = conf->fragment_size;
1295 : 122 : sm->pwd_group = conf->pwd_group;
1296 : 122 : sm->pbc_in_m1 = conf->pbc_in_m1;
1297 : 122 : sm->server_id = conf->server_id;
1298 : 122 : sm->server_id_len = conf->server_id_len;
1299 : :
1300 : 122 : wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
1301 : :
1302 : 122 : return sm;
1303 : : }
1304 : :
1305 : :
1306 : : /**
1307 : : * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
1308 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1309 : : *
1310 : : * This function deinitializes EAP state machine and frees all allocated
1311 : : * resources.
1312 : : */
1313 : 122 : void eap_server_sm_deinit(struct eap_sm *sm)
1314 : : {
1315 [ - + ]: 122 : if (sm == NULL)
1316 : 122 : return;
1317 : 122 : wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
1318 [ + - ][ + - ]: 122 : if (sm->m && sm->eap_method_priv)
1319 : 122 : sm->m->reset(sm, sm->eap_method_priv);
1320 : 122 : wpabuf_free(sm->eap_if.eapReqData);
1321 : 122 : os_free(sm->eap_if.eapKeyData);
1322 : 122 : wpabuf_free(sm->lastReqData);
1323 : 122 : wpabuf_free(sm->eap_if.eapRespData);
1324 : 122 : os_free(sm->identity);
1325 : 122 : os_free(sm->pac_opaque_encr_key);
1326 : 122 : os_free(sm->eap_fast_a_id);
1327 : 122 : os_free(sm->eap_fast_a_id_info);
1328 : 122 : wpabuf_free(sm->eap_if.aaaEapReqData);
1329 : 122 : wpabuf_free(sm->eap_if.aaaEapRespData);
1330 : 122 : os_free(sm->eap_if.aaaEapKeyData);
1331 : 122 : eap_user_free(sm->user);
1332 : 122 : wpabuf_free(sm->assoc_wps_ie);
1333 : 122 : wpabuf_free(sm->assoc_p2p_ie);
1334 : 122 : os_free(sm);
1335 : : }
1336 : :
1337 : :
1338 : : /**
1339 : : * eap_sm_notify_cached - Notify EAP state machine of cached PMK
1340 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1341 : : *
1342 : : * This function is called when PMKSA caching is used to skip EAP
1343 : : * authentication.
1344 : : */
1345 : 0 : void eap_sm_notify_cached(struct eap_sm *sm)
1346 : : {
1347 [ # # ]: 0 : if (sm == NULL)
1348 : 0 : return;
1349 : :
1350 : 0 : sm->EAP_state = EAP_SUCCESS;
1351 : : }
1352 : :
1353 : :
1354 : : /**
1355 : : * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
1356 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1357 : : *
1358 : : * This function is called when data for a pending EAP-Request is received.
1359 : : */
1360 : 12 : void eap_sm_pending_cb(struct eap_sm *sm)
1361 : : {
1362 [ - + ]: 12 : if (sm == NULL)
1363 : 12 : return;
1364 : 12 : wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
1365 [ + - ]: 12 : if (sm->method_pending == METHOD_PENDING_WAIT)
1366 : 12 : sm->method_pending = METHOD_PENDING_CONT;
1367 : : }
1368 : :
1369 : :
1370 : : /**
1371 : : * eap_sm_method_pending - Query whether EAP method is waiting for pending data
1372 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1373 : : * Returns: 1 if method is waiting for pending data or 0 if not
1374 : : */
1375 : 12 : int eap_sm_method_pending(struct eap_sm *sm)
1376 : : {
1377 [ - + ]: 12 : if (sm == NULL)
1378 : 0 : return 0;
1379 : 12 : return sm->method_pending == METHOD_PENDING_WAIT;
1380 : : }
1381 : :
1382 : :
1383 : : /**
1384 : : * eap_get_identity - Get the user identity (from EAP-Response/Identity)
1385 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1386 : : * @len: Buffer for returning identity length
1387 : : * Returns: Pointer to the user identity or %NULL if not available
1388 : : */
1389 : 0 : const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
1390 : : {
1391 : 0 : *len = sm->identity_len;
1392 : 0 : return sm->identity;
1393 : : }
1394 : :
1395 : :
1396 : : /**
1397 : : * eap_get_interface - Get pointer to EAP-EAPOL interface data
1398 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1399 : : * Returns: Pointer to the EAP-EAPOL interface data
1400 : : */
1401 : 122 : struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
1402 : : {
1403 : 122 : return &sm->eap_if;
1404 : : }
1405 : :
1406 : :
1407 : : /**
1408 : : * eap_server_clear_identity - Clear EAP identity information
1409 : : * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1410 : : *
1411 : : * This function can be used to clear the EAP identity information in the EAP
1412 : : * server context. This allows the EAP/Identity method to be used again after
1413 : : * EAPOL-Start or EAPOL-Logoff.
1414 : : */
1415 : 0 : void eap_server_clear_identity(struct eap_sm *sm)
1416 : : {
1417 : 0 : os_free(sm->identity);
1418 : 0 : sm->identity = NULL;
1419 : 0 : }
|