]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
762e4ce6 JM |
2 | * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) |
3 | * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi> | |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
03da66bd JM |
12 | #include "crypto/sha256.h" |
13 | #include "crypto/crypto.h" | |
3642c431 | 14 | #include "crypto/random.h" |
6fc6879b | 15 | #include "eap_common/eap_sim_common.h" |
03da66bd | 16 | #include "eap_server/eap_i.h" |
6fc6879b | 17 | #include "eap_server/eap_sim_db.h" |
6fc6879b JM |
18 | |
19 | ||
20 | struct eap_aka_data { | |
21 | u8 mk[EAP_SIM_MK_LEN]; | |
22 | u8 nonce_s[EAP_SIM_NONCE_S_LEN]; | |
a9d1364c | 23 | u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; |
6fc6879b | 24 | u8 k_encr[EAP_SIM_K_ENCR_LEN]; |
a9d1364c | 25 | u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ |
6fc6879b JM |
26 | u8 msk[EAP_SIM_KEYING_DATA_LEN]; |
27 | u8 emsk[EAP_EMSK_LEN]; | |
28 | u8 rand[EAP_AKA_RAND_LEN]; | |
29 | u8 autn[EAP_AKA_AUTN_LEN]; | |
30 | u8 ck[EAP_AKA_CK_LEN]; | |
31 | u8 ik[EAP_AKA_IK_LEN]; | |
32 | u8 res[EAP_AKA_RES_MAX_LEN]; | |
33 | size_t res_len; | |
34 | enum { | |
35 | IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE | |
36 | } state; | |
37 | char *next_pseudonym; | |
38 | char *next_reauth_id; | |
39 | u16 counter; | |
40 | struct eap_sim_reauth *reauth; | |
41 | int auts_reported; /* whether the current AUTS has been reported to the | |
42 | * eap_sim_db */ | |
43 | u16 notification; | |
44 | int use_result_ind; | |
45 | ||
46 | struct wpabuf *id_msgs; | |
47 | int pending_id; | |
a9d1364c JM |
48 | u8 eap_method; |
49 | u8 *network_name; | |
50 | size_t network_name_len; | |
51 | u16 kdf; | |
b4569a3b | 52 | int identity_round; |
8a9f58f2 | 53 | char permanent[20]; /* Permanent username */ |
6fc6879b JM |
54 | }; |
55 | ||
56 | ||
68a41bbb JM |
57 | static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); |
58 | ||
59 | ||
6fc6879b JM |
60 | static const char * eap_aka_state_txt(int state) |
61 | { | |
62 | switch (state) { | |
63 | case IDENTITY: | |
64 | return "IDENTITY"; | |
65 | case CHALLENGE: | |
66 | return "CHALLENGE"; | |
67 | case REAUTH: | |
68 | return "REAUTH"; | |
69 | case SUCCESS: | |
70 | return "SUCCESS"; | |
71 | case FAILURE: | |
72 | return "FAILURE"; | |
73 | case NOTIFICATION: | |
74 | return "NOTIFICATION"; | |
75 | default: | |
76 | return "Unknown?!"; | |
77 | } | |
78 | } | |
79 | ||
80 | ||
81 | static void eap_aka_state(struct eap_aka_data *data, int state) | |
82 | { | |
83 | wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", | |
84 | eap_aka_state_txt(data->state), | |
85 | eap_aka_state_txt(state)); | |
86 | data->state = state; | |
87 | } | |
88 | ||
89 | ||
68a41bbb JM |
90 | static int eap_aka_check_identity_reauth(struct eap_sm *sm, |
91 | struct eap_aka_data *data, | |
92 | const char *username) | |
93 | { | |
94 | if (data->eap_method == EAP_TYPE_AKA_PRIME && | |
95 | username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX) | |
96 | return 0; | |
97 | if (data->eap_method == EAP_TYPE_AKA && | |
98 | username[0] != EAP_AKA_REAUTH_ID_PREFIX) | |
99 | return 0; | |
100 | ||
101 | wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username); | |
102 | data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv, | |
103 | username); | |
104 | if (data->reauth == NULL) { | |
105 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - " | |
106 | "request full auth identity"); | |
107 | /* Remain in IDENTITY state for another round */ | |
108 | return 0; | |
109 | } | |
110 | ||
111 | wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication"); | |
112 | os_strlcpy(data->permanent, data->reauth->permanent, | |
113 | sizeof(data->permanent)); | |
114 | data->counter = data->reauth->counter; | |
115 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { | |
116 | os_memcpy(data->k_encr, data->reauth->k_encr, | |
117 | EAP_SIM_K_ENCR_LEN); | |
118 | os_memcpy(data->k_aut, data->reauth->k_aut, | |
119 | EAP_AKA_PRIME_K_AUT_LEN); | |
120 | os_memcpy(data->k_re, data->reauth->k_re, | |
121 | EAP_AKA_PRIME_K_RE_LEN); | |
122 | } else { | |
123 | os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); | |
124 | } | |
125 | ||
126 | eap_aka_state(data, REAUTH); | |
127 | return 1; | |
128 | } | |
129 | ||
130 | ||
131 | static void eap_aka_check_identity(struct eap_sm *sm, | |
132 | struct eap_aka_data *data) | |
133 | { | |
134 | char *username; | |
135 | ||
136 | /* Check if we already know the identity from EAP-Response/Identity */ | |
137 | ||
138 | username = sim_get_username(sm->identity, sm->identity_len); | |
139 | if (username == NULL) | |
140 | return; | |
141 | ||
142 | if (eap_aka_check_identity_reauth(sm, data, username) > 0) { | |
143 | os_free(username); | |
144 | /* | |
145 | * Since re-auth username was recognized, skip AKA/Identity | |
146 | * exchange. | |
147 | */ | |
148 | return; | |
149 | } | |
150 | ||
151 | if ((data->eap_method == EAP_TYPE_AKA_PRIME && | |
152 | username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || | |
153 | (data->eap_method == EAP_TYPE_AKA && | |
154 | username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { | |
155 | const char *permanent; | |
156 | wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", | |
157 | username); | |
158 | permanent = eap_sim_db_get_permanent( | |
159 | sm->eap_sim_db_priv, username); | |
160 | if (permanent == NULL) { | |
161 | os_free(username); | |
162 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " | |
163 | "identity - request permanent identity"); | |
164 | /* Remain in IDENTITY state for another round */ | |
165 | return; | |
166 | } | |
167 | os_strlcpy(data->permanent, permanent, | |
168 | sizeof(data->permanent)); | |
169 | /* | |
170 | * Since pseudonym username was recognized, skip AKA/Identity | |
171 | * exchange. | |
172 | */ | |
173 | eap_aka_fullauth(sm, data); | |
174 | } | |
175 | ||
176 | os_free(username); | |
177 | } | |
178 | ||
179 | ||
6fc6879b JM |
180 | static void * eap_aka_init(struct eap_sm *sm) |
181 | { | |
182 | struct eap_aka_data *data; | |
183 | ||
184 | if (sm->eap_sim_db_priv == NULL) { | |
185 | wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); | |
186 | return NULL; | |
187 | } | |
188 | ||
189 | data = os_zalloc(sizeof(*data)); | |
190 | if (data == NULL) | |
191 | return NULL; | |
a9d1364c JM |
192 | |
193 | data->eap_method = EAP_TYPE_AKA; | |
194 | ||
6fc6879b | 195 | data->state = IDENTITY; |
6fc6879b | 196 | data->pending_id = -1; |
68a41bbb | 197 | eap_aka_check_identity(sm, data); |
6fc6879b JM |
198 | |
199 | return data; | |
200 | } | |
201 | ||
202 | ||
1e5839e0 | 203 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c JM |
204 | static void * eap_aka_prime_init(struct eap_sm *sm) |
205 | { | |
206 | struct eap_aka_data *data; | |
207 | /* TODO: make ANID configurable; see 3GPP TS 24.302 */ | |
208 | char *network_name = "WLAN"; | |
209 | ||
210 | if (sm->eap_sim_db_priv == NULL) { | |
211 | wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); | |
212 | return NULL; | |
213 | } | |
214 | ||
215 | data = os_zalloc(sizeof(*data)); | |
216 | if (data == NULL) | |
217 | return NULL; | |
218 | ||
219 | data->eap_method = EAP_TYPE_AKA_PRIME; | |
363ab440 | 220 | data->network_name = (u8 *) os_strdup(network_name); |
a9d1364c JM |
221 | if (data->network_name == NULL) { |
222 | os_free(data); | |
223 | return NULL; | |
224 | } | |
225 | ||
226 | data->network_name_len = os_strlen(network_name); | |
a9d1364c JM |
227 | |
228 | data->state = IDENTITY; | |
a9d1364c | 229 | data->pending_id = -1; |
68a41bbb | 230 | eap_aka_check_identity(sm, data); |
a9d1364c JM |
231 | |
232 | return data; | |
233 | } | |
1e5839e0 | 234 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c JM |
235 | |
236 | ||
6fc6879b JM |
237 | static void eap_aka_reset(struct eap_sm *sm, void *priv) |
238 | { | |
239 | struct eap_aka_data *data = priv; | |
240 | os_free(data->next_pseudonym); | |
241 | os_free(data->next_reauth_id); | |
242 | wpabuf_free(data->id_msgs); | |
a9d1364c | 243 | os_free(data->network_name); |
6fc6879b JM |
244 | os_free(data); |
245 | } | |
246 | ||
247 | ||
248 | static int eap_aka_add_id_msg(struct eap_aka_data *data, | |
249 | const struct wpabuf *msg) | |
250 | { | |
251 | if (msg == NULL) | |
252 | return -1; | |
253 | ||
254 | if (data->id_msgs == NULL) { | |
255 | data->id_msgs = wpabuf_dup(msg); | |
256 | return data->id_msgs == NULL ? -1 : 0; | |
257 | } | |
258 | ||
259 | if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) | |
260 | return -1; | |
261 | wpabuf_put_buf(data->id_msgs, msg); | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | ||
267 | static void eap_aka_add_checkcode(struct eap_aka_data *data, | |
268 | struct eap_sim_msg *msg) | |
269 | { | |
270 | const u8 *addr; | |
271 | size_t len; | |
a9d1364c | 272 | u8 hash[SHA256_MAC_LEN]; |
6fc6879b JM |
273 | |
274 | wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); | |
275 | ||
276 | if (data->id_msgs == NULL) { | |
277 | /* | |
278 | * No EAP-AKA/Identity packets were exchanged - send empty | |
279 | * checkcode. | |
280 | */ | |
281 | eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); | |
282 | return; | |
283 | } | |
284 | ||
285 | /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ | |
286 | addr = wpabuf_head(data->id_msgs); | |
287 | len = wpabuf_len(data->id_msgs); | |
288 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); | |
a9d1364c JM |
289 | if (data->eap_method == EAP_TYPE_AKA_PRIME) |
290 | sha256_vector(1, &addr, &len, hash); | |
291 | else | |
292 | sha1_vector(1, &addr, &len, hash); | |
6fc6879b JM |
293 | |
294 | eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, | |
a9d1364c JM |
295 | data->eap_method == EAP_TYPE_AKA_PRIME ? |
296 | EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); | |
6fc6879b JM |
297 | } |
298 | ||
299 | ||
300 | static int eap_aka_verify_checkcode(struct eap_aka_data *data, | |
301 | const u8 *checkcode, size_t checkcode_len) | |
302 | { | |
303 | const u8 *addr; | |
304 | size_t len; | |
a9d1364c JM |
305 | u8 hash[SHA256_MAC_LEN]; |
306 | size_t hash_len; | |
6fc6879b JM |
307 | |
308 | if (checkcode == NULL) | |
309 | return -1; | |
310 | ||
311 | if (data->id_msgs == NULL) { | |
312 | if (checkcode_len != 0) { | |
313 | wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " | |
314 | "indicates that AKA/Identity messages were " | |
315 | "used, but they were not"); | |
316 | return -1; | |
317 | } | |
318 | return 0; | |
319 | } | |
320 | ||
a9d1364c JM |
321 | hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? |
322 | EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; | |
323 | ||
324 | if (checkcode_len != hash_len) { | |
6fc6879b JM |
325 | wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " |
326 | "that AKA/Identity message were not used, but they " | |
327 | "were"); | |
328 | return -1; | |
329 | } | |
330 | ||
331 | /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ | |
332 | addr = wpabuf_head(data->id_msgs); | |
333 | len = wpabuf_len(data->id_msgs); | |
a9d1364c JM |
334 | if (data->eap_method == EAP_TYPE_AKA_PRIME) |
335 | sha256_vector(1, &addr, &len, hash); | |
336 | else | |
337 | sha1_vector(1, &addr, &len, hash); | |
6fc6879b | 338 | |
a9d1364c | 339 | if (os_memcmp(hash, checkcode, hash_len) != 0) { |
6fc6879b JM |
340 | wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); |
341 | return -1; | |
342 | } | |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | ||
348 | static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, | |
349 | struct eap_aka_data *data, u8 id) | |
350 | { | |
351 | struct eap_sim_msg *msg; | |
352 | struct wpabuf *buf; | |
353 | ||
354 | wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); | |
a9d1364c | 355 | msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, |
6fc6879b | 356 | EAP_AKA_SUBTYPE_IDENTITY); |
b4569a3b JM |
357 | data->identity_round++; |
358 | if (data->identity_round == 1) { | |
6fc6879b JM |
359 | /* |
360 | * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is | |
361 | * ignored and the AKA/Identity is used to request the | |
362 | * identity. | |
363 | */ | |
364 | wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); | |
365 | eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); | |
b4569a3b JM |
366 | } else if (data->identity_round > 3) { |
367 | /* Cannot use more than three rounds of Identity messages */ | |
58fd49c0 | 368 | eap_sim_msg_free(msg); |
b4569a3b JM |
369 | return NULL; |
370 | } else if (sm->identity && sm->identity_len > 0 && | |
371 | (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX || | |
372 | sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) { | |
373 | /* Reauth id may have expired - try fullauth */ | |
374 | wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); | |
375 | eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); | |
376 | } else { | |
377 | wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); | |
378 | eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); | |
6fc6879b JM |
379 | } |
380 | buf = eap_sim_msg_finish(msg, NULL, NULL, 0); | |
381 | if (eap_aka_add_id_msg(data, buf) < 0) { | |
382 | wpabuf_free(buf); | |
383 | return NULL; | |
384 | } | |
385 | data->pending_id = id; | |
386 | return buf; | |
387 | } | |
388 | ||
389 | ||
390 | static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, | |
391 | struct eap_sim_msg *msg, u16 counter, | |
392 | const u8 *nonce_s) | |
393 | { | |
394 | os_free(data->next_pseudonym); | |
0047c047 JM |
395 | if (nonce_s == NULL) { |
396 | data->next_pseudonym = | |
762e4ce6 JM |
397 | eap_sim_db_get_next_pseudonym( |
398 | sm->eap_sim_db_priv, | |
399 | data->eap_method == EAP_TYPE_AKA_PRIME ? | |
400 | EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); | |
0047c047 JM |
401 | } else { |
402 | /* Do not update pseudonym during re-authentication */ | |
403 | data->next_pseudonym = NULL; | |
404 | } | |
6fc6879b JM |
405 | os_free(data->next_reauth_id); |
406 | if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { | |
407 | data->next_reauth_id = | |
762e4ce6 JM |
408 | eap_sim_db_get_next_reauth_id( |
409 | sm->eap_sim_db_priv, | |
410 | data->eap_method == EAP_TYPE_AKA_PRIME ? | |
411 | EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); | |
6fc6879b JM |
412 | } else { |
413 | wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " | |
414 | "count exceeded - force full authentication"); | |
415 | data->next_reauth_id = NULL; | |
416 | } | |
417 | ||
418 | if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && | |
419 | counter == 0 && nonce_s == NULL) | |
420 | return 0; | |
421 | ||
422 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
423 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
424 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); | |
425 | ||
426 | if (counter > 0) { | |
427 | wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); | |
428 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); | |
429 | } | |
430 | ||
431 | if (nonce_s) { | |
432 | wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); | |
433 | eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, | |
434 | EAP_SIM_NONCE_S_LEN); | |
435 | } | |
436 | ||
437 | if (data->next_pseudonym) { | |
438 | wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", | |
439 | data->next_pseudonym); | |
440 | eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, | |
441 | os_strlen(data->next_pseudonym), | |
442 | (u8 *) data->next_pseudonym, | |
443 | os_strlen(data->next_pseudonym)); | |
444 | } | |
445 | ||
446 | if (data->next_reauth_id) { | |
447 | wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", | |
448 | data->next_reauth_id); | |
449 | eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, | |
450 | os_strlen(data->next_reauth_id), | |
451 | (u8 *) data->next_reauth_id, | |
452 | os_strlen(data->next_reauth_id)); | |
453 | } | |
454 | ||
455 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { | |
456 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " | |
457 | "AT_ENCR_DATA"); | |
458 | return -1; | |
459 | } | |
460 | ||
461 | return 0; | |
462 | } | |
463 | ||
464 | ||
465 | static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, | |
466 | struct eap_aka_data *data, | |
467 | u8 id) | |
468 | { | |
469 | struct eap_sim_msg *msg; | |
470 | ||
471 | wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); | |
a9d1364c | 472 | msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, |
6fc6879b JM |
473 | EAP_AKA_SUBTYPE_CHALLENGE); |
474 | wpa_printf(MSG_DEBUG, " AT_RAND"); | |
475 | eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); | |
3fe430b5 | 476 | wpa_printf(MSG_DEBUG, " AT_AUTN"); |
6fc6879b | 477 | eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); |
a9d1364c JM |
478 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
479 | if (data->kdf) { | |
480 | /* Add the selected KDF into the beginning */ | |
3fe430b5 | 481 | wpa_printf(MSG_DEBUG, " AT_KDF"); |
a9d1364c JM |
482 | eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, |
483 | NULL, 0); | |
484 | } | |
3fe430b5 | 485 | wpa_printf(MSG_DEBUG, " AT_KDF"); |
a9d1364c JM |
486 | eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, |
487 | NULL, 0); | |
3fe430b5 | 488 | wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); |
a9d1364c JM |
489 | eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, |
490 | data->network_name_len, | |
491 | data->network_name, data->network_name_len); | |
492 | } | |
6fc6879b JM |
493 | |
494 | if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { | |
495 | eap_sim_msg_free(msg); | |
496 | return NULL; | |
497 | } | |
498 | ||
499 | eap_aka_add_checkcode(data, msg); | |
500 | ||
501 | if (sm->eap_sim_aka_result_ind) { | |
502 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
503 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
504 | } | |
505 | ||
1e5839e0 | 506 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c JM |
507 | if (data->eap_method == EAP_TYPE_AKA) { |
508 | u16 flags = 0; | |
509 | int i; | |
510 | int aka_prime_preferred = 0; | |
511 | ||
512 | i = 0; | |
513 | while (sm->user && i < EAP_MAX_METHODS && | |
514 | (sm->user->methods[i].vendor != EAP_VENDOR_IETF || | |
515 | sm->user->methods[i].method != EAP_TYPE_NONE)) { | |
516 | if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { | |
517 | if (sm->user->methods[i].method == | |
518 | EAP_TYPE_AKA) | |
519 | break; | |
520 | if (sm->user->methods[i].method == | |
521 | EAP_TYPE_AKA_PRIME) { | |
522 | aka_prime_preferred = 1; | |
523 | break; | |
524 | } | |
525 | } | |
526 | i++; | |
527 | } | |
528 | ||
529 | if (aka_prime_preferred) | |
530 | flags |= EAP_AKA_BIDDING_FLAG_D; | |
531 | eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); | |
532 | } | |
1e5839e0 | 533 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c | 534 | |
6fc6879b JM |
535 | wpa_printf(MSG_DEBUG, " AT_MAC"); |
536 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
537 | return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); | |
538 | } | |
539 | ||
540 | ||
541 | static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, | |
542 | struct eap_aka_data *data, u8 id) | |
543 | { | |
544 | struct eap_sim_msg *msg; | |
545 | ||
546 | wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); | |
547 | ||
3642c431 | 548 | if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) |
6fc6879b JM |
549 | return NULL; |
550 | wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", | |
551 | data->nonce_s, EAP_SIM_NONCE_S_LEN); | |
552 | ||
a9d1364c JM |
553 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
554 | eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, | |
555 | sm->identity, | |
556 | sm->identity_len, | |
557 | data->nonce_s, | |
558 | data->msk, data->emsk); | |
559 | } else { | |
560 | eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, | |
561 | data->msk, data->emsk); | |
562 | eap_sim_derive_keys_reauth(data->counter, sm->identity, | |
563 | sm->identity_len, data->nonce_s, | |
564 | data->mk, data->msk, data->emsk); | |
565 | } | |
6fc6879b | 566 | |
a9d1364c | 567 | msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, |
6fc6879b JM |
568 | EAP_AKA_SUBTYPE_REAUTHENTICATION); |
569 | ||
570 | if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { | |
571 | eap_sim_msg_free(msg); | |
572 | return NULL; | |
573 | } | |
574 | ||
575 | eap_aka_add_checkcode(data, msg); | |
576 | ||
577 | if (sm->eap_sim_aka_result_ind) { | |
578 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
579 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
580 | } | |
581 | ||
582 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
583 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
584 | return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); | |
585 | } | |
586 | ||
587 | ||
588 | static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, | |
589 | struct eap_aka_data *data, | |
590 | u8 id) | |
591 | { | |
592 | struct eap_sim_msg *msg; | |
593 | ||
594 | wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); | |
a9d1364c | 595 | msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, |
6fc6879b JM |
596 | EAP_AKA_SUBTYPE_NOTIFICATION); |
597 | wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); | |
598 | eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, | |
599 | NULL, 0); | |
600 | if (data->use_result_ind) { | |
601 | if (data->reauth) { | |
602 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
603 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
604 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, | |
605 | EAP_SIM_AT_ENCR_DATA); | |
606 | wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", | |
607 | data->counter); | |
608 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, | |
609 | NULL, 0); | |
610 | ||
611 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, | |
612 | EAP_SIM_AT_PADDING)) { | |
613 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " | |
614 | "encrypt AT_ENCR_DATA"); | |
615 | eap_sim_msg_free(msg); | |
616 | return NULL; | |
617 | } | |
618 | } | |
619 | ||
620 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
621 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
622 | } | |
623 | return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); | |
624 | } | |
625 | ||
626 | ||
627 | static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) | |
628 | { | |
629 | struct eap_aka_data *data = priv; | |
630 | ||
631 | data->auts_reported = 0; | |
632 | switch (data->state) { | |
633 | case IDENTITY: | |
634 | return eap_aka_build_identity(sm, data, id); | |
635 | case CHALLENGE: | |
636 | return eap_aka_build_challenge(sm, data, id); | |
637 | case REAUTH: | |
638 | return eap_aka_build_reauth(sm, data, id); | |
639 | case NOTIFICATION: | |
640 | return eap_aka_build_notification(sm, data, id); | |
641 | default: | |
642 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " | |
643 | "buildReq", data->state); | |
644 | break; | |
645 | } | |
646 | return NULL; | |
647 | } | |
648 | ||
649 | ||
650 | static Boolean eap_aka_check(struct eap_sm *sm, void *priv, | |
651 | struct wpabuf *respData) | |
652 | { | |
a9d1364c | 653 | struct eap_aka_data *data = priv; |
6fc6879b JM |
654 | const u8 *pos; |
655 | size_t len; | |
656 | ||
a9d1364c JM |
657 | pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, |
658 | &len); | |
6fc6879b JM |
659 | if (pos == NULL || len < 3) { |
660 | wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); | |
661 | return TRUE; | |
662 | } | |
663 | ||
664 | return FALSE; | |
665 | } | |
666 | ||
667 | ||
668 | static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) | |
669 | { | |
670 | if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || | |
671 | subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) | |
672 | return FALSE; | |
673 | ||
674 | switch (data->state) { | |
675 | case IDENTITY: | |
676 | if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { | |
677 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " | |
678 | "subtype %d", subtype); | |
679 | return TRUE; | |
680 | } | |
681 | break; | |
682 | case CHALLENGE: | |
683 | if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && | |
684 | subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { | |
685 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " | |
686 | "subtype %d", subtype); | |
687 | return TRUE; | |
688 | } | |
689 | break; | |
690 | case REAUTH: | |
691 | if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { | |
692 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " | |
693 | "subtype %d", subtype); | |
694 | return TRUE; | |
695 | } | |
696 | break; | |
697 | case NOTIFICATION: | |
698 | if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { | |
699 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " | |
700 | "subtype %d", subtype); | |
701 | return TRUE; | |
702 | } | |
703 | break; | |
704 | default: | |
705 | wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " | |
706 | "processing a response", data->state); | |
707 | return TRUE; | |
708 | } | |
709 | ||
710 | return FALSE; | |
711 | } | |
712 | ||
713 | ||
714 | static void eap_aka_determine_identity(struct eap_sm *sm, | |
8a9f58f2 | 715 | struct eap_aka_data *data) |
6fc6879b | 716 | { |
8a9f58f2 | 717 | char *username; |
6fc6879b | 718 | |
8a9f58f2 JM |
719 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", |
720 | sm->identity, sm->identity_len); | |
6fc6879b | 721 | |
8a9f58f2 JM |
722 | username = sim_get_username(sm->identity, sm->identity_len); |
723 | if (username == NULL) { | |
724 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
725 | eap_aka_state(data, NOTIFICATION); | |
726 | return; | |
6fc6879b JM |
727 | } |
728 | ||
68a41bbb | 729 | if (eap_aka_check_identity_reauth(sm, data, username) > 0) { |
8a9f58f2 | 730 | os_free(username); |
6fc6879b JM |
731 | return; |
732 | } | |
733 | ||
9bb1025a JM |
734 | if (((data->eap_method == EAP_TYPE_AKA_PRIME && |
735 | username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) || | |
736 | (data->eap_method == EAP_TYPE_AKA && | |
737 | username[0] == EAP_AKA_REAUTH_ID_PREFIX)) && | |
738 | data->identity_round == 1) { | |
739 | /* Remain in IDENTITY state for another round to request full | |
740 | * auth identity since we did not recognize reauth id */ | |
741 | os_free(username); | |
742 | return; | |
743 | } | |
744 | ||
8a9f58f2 JM |
745 | if ((data->eap_method == EAP_TYPE_AKA_PRIME && |
746 | username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || | |
747 | (data->eap_method == EAP_TYPE_AKA && | |
748 | username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { | |
61e181db | 749 | const char *permanent; |
8a9f58f2 JM |
750 | wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", |
751 | username); | |
752 | permanent = eap_sim_db_get_permanent( | |
61e181db | 753 | sm->eap_sim_db_priv, username); |
8a9f58f2 JM |
754 | os_free(username); |
755 | if (permanent == NULL) { | |
756 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " | |
757 | "identity - request permanent identity"); | |
758 | /* Remain in IDENTITY state for another round */ | |
759 | return; | |
760 | } | |
61e181db JM |
761 | os_strlcpy(data->permanent, permanent, |
762 | sizeof(data->permanent)); | |
8a9f58f2 JM |
763 | } else if ((data->eap_method == EAP_TYPE_AKA_PRIME && |
764 | username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) || | |
765 | (data->eap_method == EAP_TYPE_AKA && | |
766 | username[0] == EAP_AKA_PERMANENT_PREFIX)) { | |
767 | wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'", | |
768 | username); | |
769 | os_strlcpy(data->permanent, username, sizeof(data->permanent)); | |
770 | os_free(username); | |
771 | } else { | |
772 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", | |
773 | username); | |
774 | os_free(username); | |
775 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
776 | eap_aka_state(data, NOTIFICATION); | |
777 | return; | |
778 | } | |
ccea4fb1 | 779 | |
8a9f58f2 | 780 | eap_aka_fullauth(sm, data); |
ccea4fb1 JM |
781 | } |
782 | ||
783 | ||
8a9f58f2 | 784 | static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) |
ccea4fb1 | 785 | { |
8a9f58f2 | 786 | size_t identity_len; |
ccea4fb1 JM |
787 | int res; |
788 | ||
61e181db JM |
789 | res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent, |
790 | data->rand, data->autn, data->ik, | |
791 | data->ck, data->res, &data->res_len, sm); | |
6fc6879b JM |
792 | if (res == EAP_SIM_DB_PENDING) { |
793 | wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " | |
794 | "not yet available - pending request"); | |
795 | sm->method_pending = METHOD_PENDING_WAIT; | |
796 | return; | |
797 | } | |
798 | ||
1e5839e0 | 799 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c JM |
800 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
801 | /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the | |
802 | * needed 6-octet SQN ^AK for CK',IK' derivation */ | |
803 | eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, | |
804 | data->autn, | |
805 | data->network_name, | |
806 | data->network_name_len); | |
807 | } | |
1e5839e0 | 808 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c | 809 | |
6fc6879b JM |
810 | data->reauth = NULL; |
811 | data->counter = 0; /* reset re-auth counter since this is full auth */ | |
812 | ||
813 | if (res != 0) { | |
814 | wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " | |
815 | "authentication data for the peer"); | |
816 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
817 | eap_aka_state(data, NOTIFICATION); | |
818 | return; | |
819 | } | |
820 | if (sm->method_pending == METHOD_PENDING_WAIT) { | |
821 | wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " | |
822 | "available - abort pending wait"); | |
823 | sm->method_pending = METHOD_PENDING_NONE; | |
824 | } | |
825 | ||
826 | identity_len = sm->identity_len; | |
827 | while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { | |
828 | wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " | |
829 | "character from identity"); | |
830 | identity_len--; | |
831 | } | |
832 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", | |
833 | sm->identity, identity_len); | |
834 | ||
a9d1364c | 835 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
83519983 | 836 | eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, |
a9d1364c JM |
837 | data->ck, data->k_encr, data->k_aut, |
838 | data->k_re, data->msk, data->emsk); | |
839 | } else { | |
840 | eap_aka_derive_mk(sm->identity, identity_len, data->ik, | |
841 | data->ck, data->mk); | |
842 | eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, | |
843 | data->msk, data->emsk); | |
844 | } | |
6fc6879b JM |
845 | |
846 | eap_aka_state(data, CHALLENGE); | |
847 | } | |
848 | ||
849 | ||
850 | static void eap_aka_process_identity(struct eap_sm *sm, | |
851 | struct eap_aka_data *data, | |
852 | struct wpabuf *respData, | |
853 | struct eap_sim_attrs *attr) | |
854 | { | |
02a0ce13 JM |
855 | u8 *new_identity; |
856 | ||
6fc6879b JM |
857 | wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); |
858 | ||
859 | if (attr->mac || attr->iv || attr->encr_data) { | |
860 | wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " | |
861 | "received in EAP-Response/AKA-Identity"); | |
862 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
863 | eap_aka_state(data, NOTIFICATION); | |
864 | return; | |
865 | } | |
866 | ||
02a0ce13 JM |
867 | /* |
868 | * We always request identity with AKA/Identity, so the peer is | |
869 | * required to have replied with one. | |
870 | */ | |
871 | if (!attr->identity || attr->identity_len == 0) { | |
872 | wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any " | |
873 | "identity"); | |
874 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
875 | eap_aka_state(data, NOTIFICATION); | |
876 | return; | |
877 | } | |
878 | ||
879 | new_identity = os_malloc(attr->identity_len); | |
880 | if (new_identity == NULL) { | |
881 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
882 | eap_aka_state(data, NOTIFICATION); | |
883 | return; | |
6fc6879b | 884 | } |
02a0ce13 JM |
885 | os_free(sm->identity); |
886 | sm->identity = new_identity; | |
887 | os_memcpy(sm->identity, attr->identity, attr->identity_len); | |
888 | sm->identity_len = attr->identity_len; | |
6fc6879b | 889 | |
8a9f58f2 | 890 | eap_aka_determine_identity(sm, data); |
6fc6879b JM |
891 | if (eap_get_id(respData) == data->pending_id) { |
892 | data->pending_id = -1; | |
893 | eap_aka_add_id_msg(data, respData); | |
894 | } | |
895 | } | |
896 | ||
897 | ||
a9d1364c JM |
898 | static int eap_aka_verify_mac(struct eap_aka_data *data, |
899 | const struct wpabuf *req, | |
900 | const u8 *mac, const u8 *extra, | |
901 | size_t extra_len) | |
902 | { | |
903 | if (data->eap_method == EAP_TYPE_AKA_PRIME) | |
904 | return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, | |
905 | extra_len); | |
906 | return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); | |
907 | } | |
908 | ||
909 | ||
6fc6879b JM |
910 | static void eap_aka_process_challenge(struct eap_sm *sm, |
911 | struct eap_aka_data *data, | |
912 | struct wpabuf *respData, | |
913 | struct eap_sim_attrs *attr) | |
914 | { | |
6fc6879b JM |
915 | wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); |
916 | ||
1e5839e0 | 917 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c JM |
918 | #if 0 |
919 | /* KDF negotiation; to be enabled only after more than one KDF is | |
920 | * supported */ | |
921 | if (data->eap_method == EAP_TYPE_AKA_PRIME && | |
922 | attr->kdf_count == 1 && attr->mac == NULL) { | |
923 | if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { | |
924 | wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " | |
925 | "unknown KDF"); | |
926 | data->notification = | |
927 | EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
928 | eap_aka_state(data, NOTIFICATION); | |
929 | return; | |
930 | } | |
931 | ||
932 | data->kdf = attr->kdf[0]; | |
933 | ||
934 | /* Allow negotiation to continue with the selected KDF by | |
935 | * sending another Challenge message */ | |
936 | wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); | |
937 | return; | |
938 | } | |
939 | #endif | |
1e5839e0 | 940 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c | 941 | |
6fc6879b JM |
942 | if (attr->checkcode && |
943 | eap_aka_verify_checkcode(data, attr->checkcode, | |
944 | attr->checkcode_len)) { | |
945 | wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " | |
946 | "message"); | |
947 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
948 | eap_aka_state(data, NOTIFICATION); | |
949 | return; | |
950 | } | |
951 | if (attr->mac == NULL || | |
a9d1364c | 952 | eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { |
6fc6879b JM |
953 | wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " |
954 | "did not include valid AT_MAC"); | |
955 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
956 | eap_aka_state(data, NOTIFICATION); | |
957 | return; | |
958 | } | |
959 | ||
04a5bad6 JM |
960 | /* |
961 | * AT_RES is padded, so verify that there is enough room for RES and | |
962 | * that the RES length in bits matches with the expected RES. | |
963 | */ | |
964 | if (attr->res == NULL || attr->res_len < data->res_len || | |
965 | attr->res_len_bits != data->res_len * 8 || | |
6fc6879b JM |
966 | os_memcmp(attr->res, data->res, data->res_len) != 0) { |
967 | wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " | |
04a5bad6 JM |
968 | "include valid AT_RES (attr len=%lu, res len=%lu " |
969 | "bits, expected %lu bits)", | |
970 | (unsigned long) attr->res_len, | |
971 | (unsigned long) attr->res_len_bits, | |
6cd4f02b | 972 | (unsigned long) data->res_len * 8); |
6fc6879b JM |
973 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; |
974 | eap_aka_state(data, NOTIFICATION); | |
975 | return; | |
976 | } | |
977 | ||
978 | wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " | |
979 | "correct AT_MAC"); | |
980 | if (sm->eap_sim_aka_result_ind && attr->result_ind) { | |
981 | data->use_result_ind = 1; | |
982 | data->notification = EAP_SIM_SUCCESS; | |
983 | eap_aka_state(data, NOTIFICATION); | |
984 | } else | |
985 | eap_aka_state(data, SUCCESS); | |
986 | ||
6fc6879b | 987 | if (data->next_pseudonym) { |
61e181db | 988 | eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, |
6fc6879b JM |
989 | data->next_pseudonym); |
990 | data->next_pseudonym = NULL; | |
991 | } | |
992 | if (data->next_reauth_id) { | |
a9d1364c | 993 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
1e5839e0 | 994 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c | 995 | eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, |
61e181db | 996 | data->permanent, |
a9d1364c JM |
997 | data->next_reauth_id, |
998 | data->counter + 1, | |
999 | data->k_encr, data->k_aut, | |
1000 | data->k_re); | |
1e5839e0 | 1001 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c | 1002 | } else { |
8a9f58f2 | 1003 | eap_sim_db_add_reauth(sm->eap_sim_db_priv, |
61e181db | 1004 | data->permanent, |
a9d1364c JM |
1005 | data->next_reauth_id, |
1006 | data->counter + 1, | |
1007 | data->mk); | |
1008 | } | |
6fc6879b JM |
1009 | data->next_reauth_id = NULL; |
1010 | } | |
1011 | } | |
1012 | ||
1013 | ||
1014 | static void eap_aka_process_sync_failure(struct eap_sm *sm, | |
1015 | struct eap_aka_data *data, | |
1016 | struct wpabuf *respData, | |
1017 | struct eap_sim_attrs *attr) | |
1018 | { | |
1019 | wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); | |
1020 | ||
1021 | if (attr->auts == NULL) { | |
1022 | wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " | |
1023 | "message did not include valid AT_AUTS"); | |
1024 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
1025 | eap_aka_state(data, NOTIFICATION); | |
1026 | return; | |
1027 | } | |
1028 | ||
1029 | /* Avoid re-reporting AUTS when processing pending EAP packet by | |
1030 | * maintaining a local flag stating whether this AUTS has already been | |
1031 | * reported. */ | |
1032 | if (!data->auts_reported && | |
61e181db JM |
1033 | eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent, |
1034 | attr->auts, data->rand)) { | |
6fc6879b JM |
1035 | wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); |
1036 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
1037 | eap_aka_state(data, NOTIFICATION); | |
1038 | return; | |
1039 | } | |
1040 | data->auts_reported = 1; | |
1041 | ||
8a9f58f2 | 1042 | /* Remain in CHALLENGE state to re-try after resynchronization */ |
6fc6879b JM |
1043 | } |
1044 | ||
1045 | ||
1046 | static void eap_aka_process_reauth(struct eap_sm *sm, | |
1047 | struct eap_aka_data *data, | |
1048 | struct wpabuf *respData, | |
1049 | struct eap_sim_attrs *attr) | |
1050 | { | |
1051 | struct eap_sim_attrs eattr; | |
1052 | u8 *decrypted = NULL; | |
6fc6879b JM |
1053 | |
1054 | wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); | |
1055 | ||
1056 | if (attr->mac == NULL || | |
a9d1364c | 1057 | eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, |
6fc6879b JM |
1058 | EAP_SIM_NONCE_S_LEN)) { |
1059 | wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " | |
1060 | "did not include valid AT_MAC"); | |
1061 | goto fail; | |
1062 | } | |
1063 | ||
1064 | if (attr->encr_data == NULL || attr->iv == NULL) { | |
1065 | wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " | |
1066 | "message did not include encrypted data"); | |
1067 | goto fail; | |
1068 | } | |
1069 | ||
1070 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
1071 | attr->encr_data_len, attr->iv, &eattr, | |
1072 | 0); | |
1073 | if (decrypted == NULL) { | |
1074 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " | |
1075 | "data from reauthentication message"); | |
1076 | goto fail; | |
1077 | } | |
1078 | ||
1079 | if (eattr.counter != data->counter) { | |
1080 | wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " | |
1081 | "used incorrect counter %u, expected %u", | |
1082 | eattr.counter, data->counter); | |
1083 | goto fail; | |
1084 | } | |
1085 | os_free(decrypted); | |
1086 | decrypted = NULL; | |
1087 | ||
1088 | wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " | |
1089 | "the correct AT_MAC"); | |
1090 | ||
1091 | if (eattr.counter_too_small) { | |
1092 | wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " | |
1093 | "included AT_COUNTER_TOO_SMALL - starting full " | |
1094 | "authentication"); | |
8a9f58f2 | 1095 | eap_aka_fullauth(sm, data); |
6fc6879b JM |
1096 | return; |
1097 | } | |
1098 | ||
1099 | if (sm->eap_sim_aka_result_ind && attr->result_ind) { | |
1100 | data->use_result_ind = 1; | |
1101 | data->notification = EAP_SIM_SUCCESS; | |
1102 | eap_aka_state(data, NOTIFICATION); | |
1103 | } else | |
1104 | eap_aka_state(data, SUCCESS); | |
1105 | ||
6fc6879b | 1106 | if (data->next_reauth_id) { |
a9d1364c | 1107 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
1e5839e0 | 1108 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c | 1109 | eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, |
61e181db | 1110 | data->permanent, |
a9d1364c JM |
1111 | data->next_reauth_id, |
1112 | data->counter + 1, | |
1113 | data->k_encr, data->k_aut, | |
1114 | data->k_re); | |
1e5839e0 | 1115 | #endif /* EAP_SERVER_AKA_PRIME */ |
a9d1364c | 1116 | } else { |
8a9f58f2 | 1117 | eap_sim_db_add_reauth(sm->eap_sim_db_priv, |
61e181db | 1118 | data->permanent, |
a9d1364c JM |
1119 | data->next_reauth_id, |
1120 | data->counter + 1, | |
1121 | data->mk); | |
1122 | } | |
6fc6879b JM |
1123 | data->next_reauth_id = NULL; |
1124 | } else { | |
1125 | eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); | |
1126 | data->reauth = NULL; | |
1127 | } | |
1128 | ||
1129 | return; | |
1130 | ||
1131 | fail: | |
1132 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
1133 | eap_aka_state(data, NOTIFICATION); | |
1134 | eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); | |
1135 | data->reauth = NULL; | |
1136 | os_free(decrypted); | |
1137 | } | |
1138 | ||
1139 | ||
1140 | static void eap_aka_process_client_error(struct eap_sm *sm, | |
1141 | struct eap_aka_data *data, | |
1142 | struct wpabuf *respData, | |
1143 | struct eap_sim_attrs *attr) | |
1144 | { | |
1145 | wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", | |
1146 | attr->client_error_code); | |
1147 | if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) | |
1148 | eap_aka_state(data, SUCCESS); | |
1149 | else | |
1150 | eap_aka_state(data, FAILURE); | |
1151 | } | |
1152 | ||
1153 | ||
1154 | static void eap_aka_process_authentication_reject( | |
1155 | struct eap_sm *sm, struct eap_aka_data *data, | |
1156 | struct wpabuf *respData, struct eap_sim_attrs *attr) | |
1157 | { | |
1158 | wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); | |
1159 | eap_aka_state(data, FAILURE); | |
1160 | } | |
1161 | ||
1162 | ||
1163 | static void eap_aka_process_notification(struct eap_sm *sm, | |
1164 | struct eap_aka_data *data, | |
1165 | struct wpabuf *respData, | |
1166 | struct eap_sim_attrs *attr) | |
1167 | { | |
1168 | wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); | |
1169 | if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) | |
1170 | eap_aka_state(data, SUCCESS); | |
1171 | else | |
1172 | eap_aka_state(data, FAILURE); | |
1173 | } | |
1174 | ||
1175 | ||
1176 | static void eap_aka_process(struct eap_sm *sm, void *priv, | |
1177 | struct wpabuf *respData) | |
1178 | { | |
1179 | struct eap_aka_data *data = priv; | |
1180 | const u8 *pos, *end; | |
1181 | u8 subtype; | |
1182 | size_t len; | |
1183 | struct eap_sim_attrs attr; | |
1184 | ||
a9d1364c JM |
1185 | pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, |
1186 | &len); | |
6fc6879b JM |
1187 | if (pos == NULL || len < 3) |
1188 | return; | |
1189 | ||
1190 | end = pos + len; | |
1191 | subtype = *pos; | |
1192 | pos += 3; | |
1193 | ||
1194 | if (eap_aka_subtype_ok(data, subtype)) { | |
1195 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " | |
1196 | "EAP-AKA Subtype in EAP Response"); | |
1197 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
1198 | eap_aka_state(data, NOTIFICATION); | |
1199 | return; | |
1200 | } | |
1201 | ||
a9d1364c JM |
1202 | if (eap_sim_parse_attr(pos, end, &attr, |
1203 | data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, | |
1204 | 0)) { | |
6fc6879b JM |
1205 | wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); |
1206 | data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; | |
1207 | eap_aka_state(data, NOTIFICATION); | |
1208 | return; | |
1209 | } | |
1210 | ||
1211 | if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { | |
1212 | eap_aka_process_client_error(sm, data, respData, &attr); | |
1213 | return; | |
1214 | } | |
1215 | ||
1216 | if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { | |
1217 | eap_aka_process_authentication_reject(sm, data, respData, | |
1218 | &attr); | |
1219 | return; | |
1220 | } | |
1221 | ||
1222 | switch (data->state) { | |
1223 | case IDENTITY: | |
1224 | eap_aka_process_identity(sm, data, respData, &attr); | |
1225 | break; | |
1226 | case CHALLENGE: | |
1227 | if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { | |
1228 | eap_aka_process_sync_failure(sm, data, respData, | |
1229 | &attr); | |
1230 | } else { | |
1231 | eap_aka_process_challenge(sm, data, respData, &attr); | |
1232 | } | |
1233 | break; | |
1234 | case REAUTH: | |
1235 | eap_aka_process_reauth(sm, data, respData, &attr); | |
1236 | break; | |
1237 | case NOTIFICATION: | |
1238 | eap_aka_process_notification(sm, data, respData, &attr); | |
1239 | break; | |
1240 | default: | |
1241 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " | |
1242 | "process", data->state); | |
1243 | break; | |
1244 | } | |
1245 | } | |
1246 | ||
1247 | ||
1248 | static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) | |
1249 | { | |
1250 | struct eap_aka_data *data = priv; | |
1251 | return data->state == SUCCESS || data->state == FAILURE; | |
1252 | } | |
1253 | ||
1254 | ||
1255 | static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
1256 | { | |
1257 | struct eap_aka_data *data = priv; | |
1258 | u8 *key; | |
1259 | ||
1260 | if (data->state != SUCCESS) | |
1261 | return NULL; | |
1262 | ||
1263 | key = os_malloc(EAP_SIM_KEYING_DATA_LEN); | |
1264 | if (key == NULL) | |
1265 | return NULL; | |
1266 | os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); | |
1267 | *len = EAP_SIM_KEYING_DATA_LEN; | |
1268 | return key; | |
1269 | } | |
1270 | ||
1271 | ||
1272 | static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) | |
1273 | { | |
1274 | struct eap_aka_data *data = priv; | |
1275 | u8 *key; | |
1276 | ||
1277 | if (data->state != SUCCESS) | |
1278 | return NULL; | |
1279 | ||
1280 | key = os_malloc(EAP_EMSK_LEN); | |
1281 | if (key == NULL) | |
1282 | return NULL; | |
1283 | os_memcpy(key, data->emsk, EAP_EMSK_LEN); | |
1284 | *len = EAP_EMSK_LEN; | |
1285 | return key; | |
1286 | } | |
1287 | ||
1288 | ||
1289 | static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) | |
1290 | { | |
1291 | struct eap_aka_data *data = priv; | |
1292 | return data->state == SUCCESS; | |
1293 | } | |
1294 | ||
1295 | ||
1296 | int eap_server_aka_register(void) | |
1297 | { | |
1298 | struct eap_method *eap; | |
1299 | int ret; | |
1300 | ||
1301 | eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, | |
1302 | EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); | |
1303 | if (eap == NULL) | |
1304 | return -1; | |
1305 | ||
1306 | eap->init = eap_aka_init; | |
1307 | eap->reset = eap_aka_reset; | |
1308 | eap->buildReq = eap_aka_buildReq; | |
1309 | eap->check = eap_aka_check; | |
1310 | eap->process = eap_aka_process; | |
1311 | eap->isDone = eap_aka_isDone; | |
1312 | eap->getKey = eap_aka_getKey; | |
1313 | eap->isSuccess = eap_aka_isSuccess; | |
1314 | eap->get_emsk = eap_aka_get_emsk; | |
1315 | ||
1316 | ret = eap_server_method_register(eap); | |
1317 | if (ret) | |
1318 | eap_server_method_free(eap); | |
1319 | return ret; | |
1320 | } | |
a9d1364c JM |
1321 | |
1322 | ||
1e5839e0 | 1323 | #ifdef EAP_SERVER_AKA_PRIME |
a9d1364c JM |
1324 | int eap_server_aka_prime_register(void) |
1325 | { | |
1326 | struct eap_method *eap; | |
1327 | int ret; | |
1328 | ||
1329 | eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, | |
1330 | EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, | |
1331 | "AKA'"); | |
1332 | if (eap == NULL) | |
1333 | return -1; | |
1334 | ||
1335 | eap->init = eap_aka_prime_init; | |
1336 | eap->reset = eap_aka_reset; | |
1337 | eap->buildReq = eap_aka_buildReq; | |
1338 | eap->check = eap_aka_check; | |
1339 | eap->process = eap_aka_process; | |
1340 | eap->isDone = eap_aka_isDone; | |
1341 | eap->getKey = eap_aka_getKey; | |
1342 | eap->isSuccess = eap_aka_isSuccess; | |
1343 | eap->get_emsk = eap_aka_get_emsk; | |
1344 | ||
1345 | ret = eap_server_method_register(eap); | |
1346 | if (ret) | |
1347 | eap_server_method_free(eap); | |
1348 | ||
1349 | return ret; | |
1350 | } | |
1e5839e0 | 1351 | #endif /* EAP_SERVER_AKA_PRIME */ |