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