]>
Commit | Line | Data |
---|---|---|
6fc6879b | 1 | /* |
762e4ce6 JM |
2 | * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) |
3 | * Copyright (c) 2004-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" | |
6fc6879b | 12 | #include "pcsc_funcs.h" |
03da66bd JM |
13 | #include "crypto/crypto.h" |
14 | #include "crypto/sha1.h" | |
15 | #include "crypto/sha256.h" | |
43df4cc2 | 16 | #include "crypto/milenage.h" |
03da66bd JM |
17 | #include "eap_common/eap_sim_common.h" |
18 | #include "eap_config.h" | |
19 | #include "eap_i.h" | |
6fc6879b JM |
20 | |
21 | ||
22 | struct eap_aka_data { | |
23 | u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; | |
24 | size_t res_len; | |
25 | u8 nonce_s[EAP_SIM_NONCE_S_LEN]; | |
26 | u8 mk[EAP_SIM_MK_LEN]; | |
a9d1364c | 27 | u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; |
6fc6879b | 28 | u8 k_encr[EAP_SIM_K_ENCR_LEN]; |
a9d1364c | 29 | u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ |
6fc6879b JM |
30 | u8 msk[EAP_SIM_KEYING_DATA_LEN]; |
31 | u8 emsk[EAP_EMSK_LEN]; | |
32 | u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; | |
33 | u8 auts[EAP_AKA_AUTS_LEN]; | |
5eefa811 | 34 | u8 reauth_mac[EAP_SIM_MAC_LEN]; |
6fc6879b JM |
35 | |
36 | int num_id_req, num_notification; | |
37 | u8 *pseudonym; | |
38 | size_t pseudonym_len; | |
39 | u8 *reauth_id; | |
40 | size_t reauth_id_len; | |
41 | int reauth; | |
42 | unsigned int counter, counter_too_small; | |
43 | u8 *last_eap_identity; | |
44 | size_t last_eap_identity_len; | |
45 | enum { | |
79122f9f | 46 | CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE |
6fc6879b JM |
47 | } state; |
48 | ||
49 | struct wpabuf *id_msgs; | |
50 | int prev_id; | |
51 | int result_ind, use_result_ind; | |
02156b98 | 52 | int use_pseudonym; |
a9d1364c JM |
53 | u8 eap_method; |
54 | u8 *network_name; | |
55 | size_t network_name_len; | |
56 | u16 kdf; | |
2cfcd014 | 57 | int kdf_negotiation; |
84fccc72 AO |
58 | u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX]; |
59 | size_t last_kdf_count; | |
45f7574d | 60 | int error_code; |
6fc6879b JM |
61 | }; |
62 | ||
63 | ||
64 | #ifndef CONFIG_NO_STDOUT_DEBUG | |
65 | static const char * eap_aka_state_txt(int state) | |
66 | { | |
67 | switch (state) { | |
68 | case CONTINUE: | |
69 | return "CONTINUE"; | |
70 | case RESULT_SUCCESS: | |
71 | return "RESULT_SUCCESS"; | |
6fc6879b JM |
72 | case SUCCESS: |
73 | return "SUCCESS"; | |
74 | case FAILURE: | |
75 | return "FAILURE"; | |
76 | default: | |
77 | return "?"; | |
78 | } | |
79 | } | |
80 | #endif /* CONFIG_NO_STDOUT_DEBUG */ | |
81 | ||
82 | ||
83 | static void eap_aka_state(struct eap_aka_data *data, int state) | |
84 | { | |
85 | wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", | |
86 | eap_aka_state_txt(data->state), | |
87 | eap_aka_state_txt(state)); | |
88 | data->state = state; | |
89 | } | |
90 | ||
91 | ||
92 | static void * eap_aka_init(struct eap_sm *sm) | |
93 | { | |
94 | struct eap_aka_data *data; | |
95 | const char *phase1 = eap_get_config_phase1(sm); | |
e026159a | 96 | struct eap_peer_config *config = eap_get_config(sm); |
6fc6879b JM |
97 | |
98 | data = os_zalloc(sizeof(*data)); | |
99 | if (data == NULL) | |
100 | return NULL; | |
101 | ||
a9d1364c JM |
102 | data->eap_method = EAP_TYPE_AKA; |
103 | ||
45f7574d AE |
104 | /* Zero is a valid error code, so we need to initialize */ |
105 | data->error_code = NO_EAP_METHOD_ERROR; | |
106 | ||
6fc6879b JM |
107 | eap_aka_state(data, CONTINUE); |
108 | data->prev_id = -1; | |
109 | ||
110 | data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; | |
111 | ||
02156b98 JM |
112 | data->use_pseudonym = !sm->init_phase2; |
113 | if (config && config->anonymous_identity && data->use_pseudonym) { | |
e026159a JM |
114 | data->pseudonym = os_malloc(config->anonymous_identity_len); |
115 | if (data->pseudonym) { | |
116 | os_memcpy(data->pseudonym, config->anonymous_identity, | |
117 | config->anonymous_identity_len); | |
118 | data->pseudonym_len = config->anonymous_identity_len; | |
119 | } | |
120 | } | |
121 | ||
6fc6879b JM |
122 | return data; |
123 | } | |
124 | ||
125 | ||
a9d1364c JM |
126 | #ifdef EAP_AKA_PRIME |
127 | static void * eap_aka_prime_init(struct eap_sm *sm) | |
128 | { | |
129 | struct eap_aka_data *data = eap_aka_init(sm); | |
130 | if (data == NULL) | |
131 | return NULL; | |
132 | data->eap_method = EAP_TYPE_AKA_PRIME; | |
133 | return data; | |
134 | } | |
135 | #endif /* EAP_AKA_PRIME */ | |
136 | ||
137 | ||
f534ee08 JM |
138 | static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth) |
139 | { | |
140 | if (!reauth) { | |
141 | os_memset(data->mk, 0, EAP_SIM_MK_LEN); | |
142 | os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN); | |
143 | os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN); | |
144 | os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN); | |
145 | } | |
146 | os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN); | |
147 | os_memset(data->emsk, 0, EAP_EMSK_LEN); | |
148 | os_memset(data->autn, 0, EAP_AKA_AUTN_LEN); | |
149 | os_memset(data->auts, 0, EAP_AKA_AUTS_LEN); | |
150 | } | |
151 | ||
152 | ||
6fc6879b JM |
153 | static void eap_aka_deinit(struct eap_sm *sm, void *priv) |
154 | { | |
155 | struct eap_aka_data *data = priv; | |
156 | if (data) { | |
157 | os_free(data->pseudonym); | |
158 | os_free(data->reauth_id); | |
159 | os_free(data->last_eap_identity); | |
160 | wpabuf_free(data->id_msgs); | |
a9d1364c | 161 | os_free(data->network_name); |
f534ee08 | 162 | eap_aka_clear_keys(data, 0); |
6fc6879b JM |
163 | os_free(data); |
164 | } | |
165 | } | |
166 | ||
167 | ||
db136058 JM |
168 | static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) |
169 | { | |
170 | char req[200], *pos, *end; | |
171 | ||
172 | wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); | |
173 | pos = req; | |
174 | end = pos + sizeof(req); | |
175 | pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); | |
176 | pos += os_snprintf(pos, end - pos, ":"); | |
177 | pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); | |
178 | pos += os_snprintf(pos, end - pos, ":"); | |
28bfa291 | 179 | wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); |
db136058 JM |
180 | |
181 | eap_sm_request_sim(sm, req); | |
182 | return 1; | |
183 | } | |
184 | ||
185 | ||
186 | static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, | |
187 | struct eap_peer_config *conf) | |
188 | { | |
189 | char *resp, *pos; | |
190 | ||
191 | wpa_printf(MSG_DEBUG, | |
192 | "EAP-AKA: Use result from external USIM processing"); | |
193 | ||
194 | resp = conf->external_sim_resp; | |
195 | conf->external_sim_resp = NULL; | |
196 | ||
197 | if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { | |
198 | pos = resp + 10; | |
199 | if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) | |
200 | goto invalid; | |
201 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, | |
202 | EAP_AKA_AUTS_LEN); | |
203 | os_free(resp); | |
204 | return -2; | |
205 | } | |
206 | ||
207 | if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { | |
208 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); | |
209 | os_free(resp); | |
210 | return -1; | |
211 | } | |
212 | ||
213 | pos = resp + 10; | |
214 | wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); | |
215 | ||
216 | if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) | |
217 | goto invalid; | |
218 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); | |
219 | pos += EAP_AKA_IK_LEN * 2; | |
220 | if (*pos != ':') | |
221 | goto invalid; | |
222 | pos++; | |
223 | ||
224 | if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) | |
225 | goto invalid; | |
226 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); | |
227 | pos += EAP_AKA_CK_LEN * 2; | |
228 | if (*pos != ':') | |
229 | goto invalid; | |
230 | pos++; | |
231 | ||
232 | data->res_len = os_strlen(pos) / 2; | |
233 | if (data->res_len > EAP_AKA_RES_MAX_LEN) { | |
234 | data->res_len = 0; | |
235 | goto invalid; | |
236 | } | |
237 | if (hexstr2bin(pos, data->res, data->res_len) < 0) | |
238 | goto invalid; | |
239 | wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); | |
240 | ||
241 | os_free(resp); | |
242 | return 0; | |
243 | ||
244 | invalid: | |
245 | wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); | |
246 | os_free(resp); | |
247 | return -1; | |
248 | } | |
249 | ||
250 | ||
6fc6879b JM |
251 | static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) |
252 | { | |
2a24bb31 JM |
253 | struct eap_peer_config *conf; |
254 | ||
6fc6879b | 255 | wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); |
2a24bb31 JM |
256 | |
257 | conf = eap_get_config(sm); | |
258 | if (conf == NULL) | |
259 | return -1; | |
db136058 JM |
260 | |
261 | if (sm->external_sim) { | |
262 | if (conf->external_sim_resp) | |
263 | return eap_aka_ext_sim_result(sm, data, conf); | |
264 | else | |
265 | return eap_aka_ext_sim_req(sm, data); | |
266 | } | |
267 | ||
2a24bb31 JM |
268 | if (conf->pcsc) { |
269 | return scard_umts_auth(sm->scard_ctx, data->rand, | |
270 | data->autn, data->res, &data->res_len, | |
271 | data->ik, data->ck, data->auts); | |
272 | } | |
273 | ||
274 | #ifdef CONFIG_USIM_SIMULATOR | |
275 | if (conf->password) { | |
276 | u8 opc[16], k[16], sqn[6]; | |
277 | const char *pos; | |
278 | wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " | |
279 | "implementation for UMTS authentication"); | |
280 | if (conf->password_len < 78) { | |
281 | wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " | |
282 | "password"); | |
283 | return -1; | |
284 | } | |
285 | pos = (const char *) conf->password; | |
286 | if (hexstr2bin(pos, k, 16)) | |
287 | return -1; | |
288 | pos += 32; | |
289 | if (*pos != ':') | |
290 | return -1; | |
291 | pos++; | |
292 | ||
293 | if (hexstr2bin(pos, opc, 16)) | |
294 | return -1; | |
295 | pos += 32; | |
296 | if (*pos != ':') | |
297 | return -1; | |
298 | pos++; | |
299 | ||
300 | if (hexstr2bin(pos, sqn, 6)) | |
301 | return -1; | |
302 | ||
303 | return milenage_check(opc, k, sqn, data->rand, data->autn, | |
304 | data->ik, data->ck, | |
305 | data->res, &data->res_len, data->auts); | |
306 | } | |
307 | #endif /* CONFIG_USIM_SIMULATOR */ | |
308 | ||
309 | #ifdef CONFIG_USIM_HARDCODED | |
310 | wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " | |
311 | "testing"); | |
312 | ||
6fc6879b JM |
313 | /* These hardcoded Kc and SRES values are used for testing. |
314 | * Could consider making them configurable. */ | |
315 | os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); | |
316 | data->res_len = EAP_AKA_RES_MAX_LEN; | |
317 | os_memset(data->ik, '3', EAP_AKA_IK_LEN); | |
318 | os_memset(data->ck, '4', EAP_AKA_CK_LEN); | |
319 | { | |
320 | u8 autn[EAP_AKA_AUTN_LEN]; | |
321 | os_memset(autn, '1', EAP_AKA_AUTN_LEN); | |
05c79d6a | 322 | if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { |
6fc6879b JM |
323 | wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " |
324 | "with expected value"); | |
325 | return -1; | |
326 | } | |
327 | } | |
328 | #if 0 | |
329 | { | |
330 | static int test_resync = 1; | |
331 | if (test_resync) { | |
332 | /* Test Resynchronization */ | |
333 | test_resync = 0; | |
334 | return -2; | |
335 | } | |
336 | } | |
337 | #endif | |
338 | return 0; | |
2a24bb31 JM |
339 | |
340 | #else /* CONFIG_USIM_HARDCODED */ | |
341 | ||
891330fd | 342 | wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm " |
2a24bb31 JM |
343 | "enabled"); |
344 | return -1; | |
345 | ||
346 | #endif /* CONFIG_USIM_HARDCODED */ | |
6fc6879b JM |
347 | } |
348 | ||
349 | ||
350 | #define CLEAR_PSEUDONYM 0x01 | |
351 | #define CLEAR_REAUTH_ID 0x02 | |
352 | #define CLEAR_EAP_ID 0x04 | |
353 | ||
e026159a JM |
354 | static void eap_aka_clear_identities(struct eap_sm *sm, |
355 | struct eap_aka_data *data, int id) | |
6fc6879b | 356 | { |
05c15c89 | 357 | if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { |
8b41e056 | 358 | wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); |
6fc6879b JM |
359 | os_free(data->pseudonym); |
360 | data->pseudonym = NULL; | |
361 | data->pseudonym_len = 0; | |
02156b98 JM |
362 | if (data->use_pseudonym) |
363 | eap_set_anon_id(sm, NULL, 0); | |
6fc6879b | 364 | } |
05c15c89 | 365 | if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { |
8b41e056 | 366 | wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); |
6fc6879b JM |
367 | os_free(data->reauth_id); |
368 | data->reauth_id = NULL; | |
369 | data->reauth_id_len = 0; | |
370 | } | |
05c15c89 | 371 | if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { |
8b41e056 | 372 | wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); |
6fc6879b JM |
373 | os_free(data->last_eap_identity); |
374 | data->last_eap_identity = NULL; | |
375 | data->last_eap_identity_len = 0; | |
376 | } | |
377 | } | |
378 | ||
379 | ||
4ac384c5 | 380 | static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, |
6fc6879b JM |
381 | struct eap_sim_attrs *attr) |
382 | { | |
383 | if (attr->next_pseudonym) { | |
4ac384c5 SB |
384 | const u8 *identity = NULL; |
385 | size_t identity_len = 0; | |
386 | const u8 *realm = NULL; | |
387 | size_t realm_len = 0; | |
388 | ||
389 | wpa_hexdump_ascii(MSG_DEBUG, | |
390 | "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", | |
391 | attr->next_pseudonym, | |
392 | attr->next_pseudonym_len); | |
6fc6879b | 393 | os_free(data->pseudonym); |
4ac384c5 SB |
394 | /* Look for the realm of the permanent identity */ |
395 | identity = eap_get_config_identity(sm, &identity_len); | |
396 | if (identity) { | |
397 | for (realm = identity, realm_len = identity_len; | |
398 | realm_len > 0; realm_len--, realm++) { | |
399 | if (*realm == '@') | |
400 | break; | |
401 | } | |
402 | } | |
403 | data->pseudonym = os_malloc(attr->next_pseudonym_len + | |
404 | realm_len); | |
6fc6879b JM |
405 | if (data->pseudonym == NULL) { |
406 | wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " | |
407 | "next pseudonym"); | |
4ac384c5 | 408 | data->pseudonym_len = 0; |
6fc6879b JM |
409 | return -1; |
410 | } | |
411 | os_memcpy(data->pseudonym, attr->next_pseudonym, | |
412 | attr->next_pseudonym_len); | |
4ac384c5 SB |
413 | if (realm_len) { |
414 | os_memcpy(data->pseudonym + attr->next_pseudonym_len, | |
415 | realm, realm_len); | |
416 | } | |
417 | data->pseudonym_len = attr->next_pseudonym_len + realm_len; | |
02156b98 JM |
418 | if (data->use_pseudonym) |
419 | eap_set_anon_id(sm, data->pseudonym, | |
420 | data->pseudonym_len); | |
6fc6879b JM |
421 | } |
422 | ||
423 | if (attr->next_reauth_id) { | |
424 | os_free(data->reauth_id); | |
a1f11e34 JB |
425 | data->reauth_id = os_memdup(attr->next_reauth_id, |
426 | attr->next_reauth_id_len); | |
6fc6879b JM |
427 | if (data->reauth_id == NULL) { |
428 | wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " | |
429 | "next reauth_id"); | |
4ac384c5 | 430 | data->reauth_id_len = 0; |
6fc6879b JM |
431 | return -1; |
432 | } | |
6fc6879b JM |
433 | data->reauth_id_len = attr->next_reauth_id_len; |
434 | wpa_hexdump_ascii(MSG_DEBUG, | |
435 | "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", | |
436 | data->reauth_id, | |
437 | data->reauth_id_len); | |
438 | } | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
443 | ||
444 | static int eap_aka_add_id_msg(struct eap_aka_data *data, | |
445 | const struct wpabuf *msg) | |
446 | { | |
447 | if (msg == NULL) | |
448 | return -1; | |
449 | ||
450 | if (data->id_msgs == NULL) { | |
451 | data->id_msgs = wpabuf_dup(msg); | |
452 | return data->id_msgs == NULL ? -1 : 0; | |
453 | } | |
454 | ||
455 | if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) | |
456 | return -1; | |
457 | wpabuf_put_buf(data->id_msgs, msg); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | ||
463 | static void eap_aka_add_checkcode(struct eap_aka_data *data, | |
464 | struct eap_sim_msg *msg) | |
465 | { | |
466 | const u8 *addr; | |
467 | size_t len; | |
a9d1364c | 468 | u8 hash[SHA256_MAC_LEN]; |
6fc6879b JM |
469 | |
470 | wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); | |
471 | ||
472 | if (data->id_msgs == NULL) { | |
473 | /* | |
474 | * No EAP-AKA/Identity packets were exchanged - send empty | |
475 | * checkcode. | |
476 | */ | |
477 | eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); | |
478 | return; | |
479 | } | |
480 | ||
a9d1364c | 481 | /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ |
6fc6879b JM |
482 | addr = wpabuf_head(data->id_msgs); |
483 | len = wpabuf_len(data->id_msgs); | |
484 | wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); | |
3ee81d48 | 485 | #ifdef EAP_AKA_PRIME |
a9d1364c JM |
486 | if (data->eap_method == EAP_TYPE_AKA_PRIME) |
487 | sha256_vector(1, &addr, &len, hash); | |
488 | else | |
3ee81d48 | 489 | #endif /* EAP_AKA_PRIME */ |
a9d1364c | 490 | sha1_vector(1, &addr, &len, hash); |
6fc6879b JM |
491 | |
492 | eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, | |
a9d1364c JM |
493 | data->eap_method == EAP_TYPE_AKA_PRIME ? |
494 | EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); | |
6fc6879b JM |
495 | } |
496 | ||
497 | ||
498 | static int eap_aka_verify_checkcode(struct eap_aka_data *data, | |
499 | const u8 *checkcode, size_t checkcode_len) | |
500 | { | |
501 | const u8 *addr; | |
502 | size_t len; | |
a9d1364c JM |
503 | u8 hash[SHA256_MAC_LEN]; |
504 | size_t hash_len; | |
6fc6879b JM |
505 | |
506 | if (checkcode == NULL) | |
507 | return -1; | |
508 | ||
509 | if (data->id_msgs == NULL) { | |
510 | if (checkcode_len != 0) { | |
511 | wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " | |
512 | "indicates that AKA/Identity messages were " | |
513 | "used, but they were not"); | |
514 | return -1; | |
515 | } | |
516 | return 0; | |
517 | } | |
518 | ||
a9d1364c JM |
519 | hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? |
520 | EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; | |
521 | ||
522 | if (checkcode_len != hash_len) { | |
6fc6879b JM |
523 | wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " |
524 | "indicates that AKA/Identity message were not " | |
525 | "used, but they were"); | |
526 | return -1; | |
527 | } | |
528 | ||
a9d1364c | 529 | /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ |
6fc6879b JM |
530 | addr = wpabuf_head(data->id_msgs); |
531 | len = wpabuf_len(data->id_msgs); | |
3ee81d48 | 532 | #ifdef EAP_AKA_PRIME |
a9d1364c JM |
533 | if (data->eap_method == EAP_TYPE_AKA_PRIME) |
534 | sha256_vector(1, &addr, &len, hash); | |
535 | else | |
3ee81d48 | 536 | #endif /* EAP_AKA_PRIME */ |
a9d1364c | 537 | sha1_vector(1, &addr, &len, hash); |
6fc6879b | 538 | |
05c79d6a | 539 | if (os_memcmp_const(hash, checkcode, hash_len) != 0) { |
6fc6879b JM |
540 | wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); |
541 | return -1; | |
542 | } | |
543 | ||
544 | return 0; | |
545 | } | |
546 | ||
547 | ||
548 | static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, | |
549 | int err) | |
550 | { | |
551 | struct eap_sim_msg *msg; | |
552 | ||
553 | eap_aka_state(data, FAILURE); | |
554 | data->num_id_req = 0; | |
555 | data->num_notification = 0; | |
556 | ||
93434989 JM |
557 | wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", |
558 | err); | |
a9d1364c | 559 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
560 | EAP_AKA_SUBTYPE_CLIENT_ERROR); |
561 | eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); | |
b2b8a4cb | 562 | return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); |
6fc6879b JM |
563 | } |
564 | ||
565 | ||
566 | static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, | |
567 | u8 id) | |
568 | { | |
569 | struct eap_sim_msg *msg; | |
570 | ||
571 | eap_aka_state(data, FAILURE); | |
572 | data->num_id_req = 0; | |
573 | data->num_notification = 0; | |
574 | ||
575 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " | |
576 | "(id=%d)", id); | |
a9d1364c | 577 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b | 578 | EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); |
b2b8a4cb | 579 | return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); |
6fc6879b JM |
580 | } |
581 | ||
582 | ||
583 | static struct wpabuf * eap_aka_synchronization_failure( | |
446600c3 | 584 | struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr) |
6fc6879b JM |
585 | { |
586 | struct eap_sim_msg *msg; | |
587 | ||
588 | data->num_id_req = 0; | |
589 | data->num_notification = 0; | |
590 | ||
591 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " | |
592 | "(id=%d)", id); | |
a9d1364c | 593 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
594 | EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); |
595 | wpa_printf(MSG_DEBUG, " AT_AUTS"); | |
596 | eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, | |
597 | EAP_AKA_AUTS_LEN); | |
446600c3 AO |
598 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
599 | size_t i; | |
600 | ||
601 | for (i = 0; i < attr->kdf_count; i++) { | |
602 | wpa_printf(MSG_DEBUG, " AT_KDF"); | |
603 | eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i], | |
604 | NULL, 0); | |
605 | } | |
606 | } | |
b2b8a4cb | 607 | return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); |
6fc6879b JM |
608 | } |
609 | ||
610 | ||
611 | static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, | |
612 | struct eap_aka_data *data, | |
613 | u8 id, | |
614 | enum eap_sim_id_req id_req) | |
615 | { | |
616 | const u8 *identity = NULL; | |
617 | size_t identity_len = 0; | |
618 | struct eap_sim_msg *msg; | |
619 | ||
620 | data->reauth = 0; | |
621 | if (id_req == ANY_ID && data->reauth_id) { | |
622 | identity = data->reauth_id; | |
623 | identity_len = data->reauth_id_len; | |
624 | data->reauth = 1; | |
625 | } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && | |
4df41339 HS |
626 | data->pseudonym && |
627 | !eap_sim_anonymous_username(data->pseudonym, | |
628 | data->pseudonym_len)) { | |
6fc6879b JM |
629 | identity = data->pseudonym; |
630 | identity_len = data->pseudonym_len; | |
e026159a | 631 | eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); |
6fc6879b JM |
632 | } else if (id_req != NO_ID_REQ) { |
633 | identity = eap_get_config_identity(sm, &identity_len); | |
634 | if (identity) { | |
e026159a | 635 | eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | |
6fc6879b JM |
636 | CLEAR_REAUTH_ID); |
637 | } | |
638 | } | |
639 | if (id_req != NO_ID_REQ) | |
e026159a | 640 | eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); |
6fc6879b JM |
641 | |
642 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); | |
a9d1364c | 643 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
644 | EAP_AKA_SUBTYPE_IDENTITY); |
645 | ||
646 | if (identity) { | |
647 | wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", | |
648 | identity, identity_len); | |
649 | eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, | |
650 | identity, identity_len); | |
651 | } | |
652 | ||
b2b8a4cb | 653 | return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); |
6fc6879b JM |
654 | } |
655 | ||
656 | ||
657 | static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, | |
658 | u8 id) | |
659 | { | |
660 | struct eap_sim_msg *msg; | |
661 | ||
662 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); | |
a9d1364c | 663 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
664 | EAP_AKA_SUBTYPE_CHALLENGE); |
665 | wpa_printf(MSG_DEBUG, " AT_RES"); | |
fa71a1d8 | 666 | eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, |
6fc6879b JM |
667 | data->res, data->res_len); |
668 | eap_aka_add_checkcode(data, msg); | |
669 | if (data->use_result_ind) { | |
670 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
671 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
672 | } | |
673 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
674 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
b2b8a4cb JM |
675 | return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "", |
676 | 0); | |
6fc6879b JM |
677 | } |
678 | ||
679 | ||
680 | static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, | |
681 | u8 id, int counter_too_small, | |
682 | const u8 *nonce_s) | |
683 | { | |
684 | struct eap_sim_msg *msg; | |
685 | unsigned int counter; | |
686 | ||
687 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", | |
688 | id); | |
a9d1364c | 689 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
690 | EAP_AKA_SUBTYPE_REAUTHENTICATION); |
691 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
692 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
693 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); | |
694 | ||
695 | if (counter_too_small) { | |
696 | wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); | |
697 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); | |
698 | counter = data->counter_too_small; | |
699 | } else | |
700 | counter = data->counter; | |
701 | ||
702 | wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); | |
703 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); | |
704 | ||
705 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { | |
706 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " | |
707 | "AT_ENCR_DATA"); | |
708 | eap_sim_msg_free(msg); | |
709 | return NULL; | |
710 | } | |
711 | eap_aka_add_checkcode(data, msg); | |
712 | if (data->use_result_ind) { | |
713 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
714 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
715 | } | |
716 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
717 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
b2b8a4cb | 718 | return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s, |
6fc6879b JM |
719 | EAP_SIM_NONCE_S_LEN); |
720 | } | |
721 | ||
722 | ||
723 | static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, | |
724 | u8 id, u16 notification) | |
725 | { | |
726 | struct eap_sim_msg *msg; | |
727 | u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; | |
728 | ||
729 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); | |
a9d1364c | 730 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, |
6fc6879b JM |
731 | EAP_AKA_SUBTYPE_NOTIFICATION); |
732 | if (k_aut && data->reauth) { | |
733 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
734 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
735 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, | |
736 | EAP_SIM_AT_ENCR_DATA); | |
737 | wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); | |
738 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, | |
739 | NULL, 0); | |
740 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, | |
741 | EAP_SIM_AT_PADDING)) { | |
742 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " | |
743 | "AT_ENCR_DATA"); | |
744 | eap_sim_msg_free(msg); | |
745 | return NULL; | |
746 | } | |
747 | } | |
748 | if (k_aut) { | |
749 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
750 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
751 | } | |
b2b8a4cb | 752 | return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0); |
6fc6879b JM |
753 | } |
754 | ||
755 | ||
756 | static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, | |
757 | struct eap_aka_data *data, | |
758 | u8 id, | |
759 | const struct wpabuf *reqData, | |
760 | struct eap_sim_attrs *attr) | |
761 | { | |
762 | int id_error; | |
763 | struct wpabuf *buf; | |
764 | ||
765 | wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); | |
766 | ||
767 | id_error = 0; | |
768 | switch (attr->id_req) { | |
769 | case NO_ID_REQ: | |
770 | break; | |
771 | case ANY_ID: | |
772 | if (data->num_id_req > 0) | |
773 | id_error++; | |
774 | data->num_id_req++; | |
775 | break; | |
776 | case FULLAUTH_ID: | |
777 | if (data->num_id_req > 1) | |
778 | id_error++; | |
779 | data->num_id_req++; | |
780 | break; | |
781 | case PERMANENT_ID: | |
782 | if (data->num_id_req > 2) | |
783 | id_error++; | |
784 | data->num_id_req++; | |
785 | break; | |
786 | } | |
787 | if (id_error) { | |
788 | wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " | |
789 | "used within one authentication"); | |
790 | return eap_aka_client_error(data, id, | |
791 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
792 | } | |
793 | ||
794 | buf = eap_aka_response_identity(sm, data, id, attr->id_req); | |
795 | ||
796 | if (data->prev_id != id) { | |
797 | eap_aka_add_id_msg(data, reqData); | |
798 | eap_aka_add_id_msg(data, buf); | |
799 | data->prev_id = id; | |
800 | } | |
801 | ||
802 | return buf; | |
803 | } | |
804 | ||
805 | ||
a9d1364c JM |
806 | static int eap_aka_verify_mac(struct eap_aka_data *data, |
807 | const struct wpabuf *req, | |
808 | const u8 *mac, const u8 *extra, | |
809 | size_t extra_len) | |
810 | { | |
811 | if (data->eap_method == EAP_TYPE_AKA_PRIME) | |
812 | return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, | |
813 | extra_len); | |
814 | return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); | |
815 | } | |
816 | ||
817 | ||
818 | #ifdef EAP_AKA_PRIME | |
819 | static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, | |
820 | u8 id, u16 kdf) | |
821 | { | |
822 | struct eap_sim_msg *msg; | |
823 | ||
2cfcd014 | 824 | data->kdf_negotiation = 1; |
a9d1364c JM |
825 | data->kdf = kdf; |
826 | wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " | |
827 | "select)", id); | |
828 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, | |
829 | EAP_AKA_SUBTYPE_CHALLENGE); | |
830 | wpa_printf(MSG_DEBUG, " AT_KDF"); | |
831 | eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); | |
b2b8a4cb | 832 | return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); |
a9d1364c JM |
833 | } |
834 | ||
835 | ||
836 | static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, | |
837 | u8 id, struct eap_sim_attrs *attr) | |
838 | { | |
839 | size_t i; | |
840 | ||
841 | for (i = 0; i < attr->kdf_count; i++) { | |
84fccc72 AO |
842 | if (attr->kdf[i] == EAP_AKA_PRIME_KDF) { |
843 | os_memcpy(data->last_kdf_attrs, attr->kdf, | |
844 | sizeof(u16) * attr->kdf_count); | |
845 | data->last_kdf_count = attr->kdf_count; | |
a9d1364c JM |
846 | return eap_aka_prime_kdf_select(data, id, |
847 | EAP_AKA_PRIME_KDF); | |
84fccc72 | 848 | } |
a9d1364c JM |
849 | } |
850 | ||
851 | /* No matching KDF found - fail authentication as if AUTN had been | |
852 | * incorrect */ | |
853 | return eap_aka_authentication_reject(data, id); | |
854 | } | |
855 | ||
856 | ||
857 | static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, | |
858 | struct eap_sim_attrs *attr) | |
859 | { | |
860 | size_t i, j; | |
861 | ||
862 | if (attr->kdf_count == 0) | |
863 | return 0; | |
864 | ||
865 | /* The only allowed (and required) duplication of a KDF is the addition | |
866 | * of the selected KDF into the beginning of the list. */ | |
867 | ||
2cfcd014 | 868 | if (data->kdf_negotiation) { |
84fccc72 AO |
869 | /* When the peer receives the new EAP-Request/AKA'-Challenge |
870 | * message, must check only requested change occurred in the | |
871 | * list of AT_KDF attributes. If there are any other changes, | |
872 | * the peer must behave like the case that AT_MAC had been | |
873 | * incorrect and authentication is failed. These are defined in | |
874 | * EAP-AKA' specification RFC 5448, Section 3.2. */ | |
a9d1364c JM |
875 | if (attr->kdf[0] != data->kdf) { |
876 | wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " | |
877 | "accept the selected KDF"); | |
84fccc72 | 878 | return -1; |
a9d1364c JM |
879 | } |
880 | ||
84fccc72 AO |
881 | if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX || |
882 | attr->kdf_count != data->last_kdf_count + 1) { | |
883 | wpa_printf(MSG_WARNING, | |
884 | "EAP-AKA': The length of KDF attributes is wrong"); | |
885 | return -1; | |
a9d1364c JM |
886 | } |
887 | ||
84fccc72 AO |
888 | for (i = 1; i < attr->kdf_count; i++) { |
889 | if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) { | |
890 | wpa_printf(MSG_WARNING, | |
891 | "EAP-AKA': The KDF attributes except selected KDF are not same as original one"); | |
892 | return -1; | |
893 | } | |
894 | } | |
a9d1364c JM |
895 | } |
896 | ||
897 | for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { | |
898 | for (j = i + 1; j < attr->kdf_count; j++) { | |
899 | if (attr->kdf[i] == attr->kdf[j]) { | |
900 | wpa_printf(MSG_WARNING, "EAP-AKA': The server " | |
901 | "included a duplicated KDF"); | |
902 | return 0; | |
903 | } | |
904 | } | |
905 | } | |
906 | ||
907 | return 1; | |
908 | } | |
909 | #endif /* EAP_AKA_PRIME */ | |
910 | ||
911 | ||
6fc6879b JM |
912 | static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, |
913 | struct eap_aka_data *data, | |
914 | u8 id, | |
915 | const struct wpabuf *reqData, | |
916 | struct eap_sim_attrs *attr) | |
917 | { | |
918 | const u8 *identity; | |
919 | size_t identity_len; | |
920 | int res; | |
921 | struct eap_sim_attrs eattr; | |
922 | ||
923 | wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); | |
924 | ||
925 | if (attr->checkcode && | |
926 | eap_aka_verify_checkcode(data, attr->checkcode, | |
927 | attr->checkcode_len)) { | |
928 | wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " | |
929 | "message"); | |
930 | return eap_aka_client_error(data, id, | |
931 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
932 | } | |
933 | ||
a9d1364c JM |
934 | #ifdef EAP_AKA_PRIME |
935 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { | |
936 | if (!attr->kdf_input || attr->kdf_input_len == 0) { | |
937 | wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " | |
938 | "did not include non-empty AT_KDF_INPUT"); | |
939 | /* Fail authentication as if AUTN had been incorrect */ | |
940 | return eap_aka_authentication_reject(data, id); | |
941 | } | |
942 | os_free(data->network_name); | |
a1f11e34 JB |
943 | data->network_name = os_memdup(attr->kdf_input, |
944 | attr->kdf_input_len); | |
a9d1364c JM |
945 | if (data->network_name == NULL) { |
946 | wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " | |
947 | "storing Network Name"); | |
948 | return eap_aka_authentication_reject(data, id); | |
949 | } | |
a9d1364c JM |
950 | data->network_name_len = attr->kdf_input_len; |
951 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " | |
952 | "(AT_KDF_INPUT)", | |
953 | data->network_name, data->network_name_len); | |
954 | /* TODO: check Network Name per 3GPP.33.402 */ | |
955 | ||
84fccc72 AO |
956 | res = eap_aka_prime_kdf_valid(data, attr); |
957 | if (res == 0) | |
a9d1364c | 958 | return eap_aka_authentication_reject(data, id); |
84fccc72 AO |
959 | else if (res == -1) |
960 | return eap_aka_client_error( | |
961 | data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
a9d1364c JM |
962 | |
963 | if (attr->kdf[0] != EAP_AKA_PRIME_KDF) | |
964 | return eap_aka_prime_kdf_neg(data, id, attr); | |
965 | ||
966 | data->kdf = EAP_AKA_PRIME_KDF; | |
967 | wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); | |
968 | } | |
969 | ||
970 | if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { | |
971 | u16 flags = WPA_GET_BE16(attr->bidding); | |
972 | if ((flags & EAP_AKA_BIDDING_FLAG_D) && | |
973 | eap_allowed_method(sm, EAP_VENDOR_IETF, | |
974 | EAP_TYPE_AKA_PRIME)) { | |
975 | wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " | |
976 | "AKA' to AKA detected"); | |
977 | /* Fail authentication as if AUTN had been incorrect */ | |
978 | return eap_aka_authentication_reject(data, id); | |
979 | } | |
980 | } | |
981 | #endif /* EAP_AKA_PRIME */ | |
982 | ||
6fc6879b JM |
983 | data->reauth = 0; |
984 | if (!attr->mac || !attr->rand || !attr->autn) { | |
985 | wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " | |
986 | "did not include%s%s%s", | |
987 | !attr->mac ? " AT_MAC" : "", | |
988 | !attr->rand ? " AT_RAND" : "", | |
989 | !attr->autn ? " AT_AUTN" : ""); | |
990 | return eap_aka_client_error(data, id, | |
991 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
992 | } | |
993 | os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); | |
994 | os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); | |
995 | ||
996 | res = eap_aka_umts_auth(sm, data); | |
997 | if (res == -1) { | |
998 | wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " | |
999 | "failed (AUTN)"); | |
1000 | return eap_aka_authentication_reject(data, id); | |
1001 | } else if (res == -2) { | |
1002 | wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " | |
1003 | "failed (AUTN seq# -> AUTS)"); | |
446600c3 | 1004 | return eap_aka_synchronization_failure(data, id, attr); |
db136058 JM |
1005 | } else if (res > 0) { |
1006 | wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); | |
1007 | return NULL; | |
6fc6879b JM |
1008 | } else if (res) { |
1009 | wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); | |
1010 | return eap_aka_client_error(data, id, | |
1011 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1012 | } | |
a9d1364c JM |
1013 | #ifdef EAP_AKA_PRIME |
1014 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { | |
1015 | /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the | |
35f30422 JM |
1016 | * needed 6-octet SQN ^ AK for CK',IK' derivation */ |
1017 | u16 amf = WPA_GET_BE16(data->autn + 6); | |
1018 | if (!(amf & 0x8000)) { | |
1019 | wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " | |
1020 | "not set (AMF=0x%4x)", amf); | |
1021 | return eap_aka_authentication_reject(data, id); | |
1022 | } | |
a9d1364c JM |
1023 | eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, |
1024 | data->autn, | |
1025 | data->network_name, | |
1026 | data->network_name_len); | |
1027 | } | |
1028 | #endif /* EAP_AKA_PRIME */ | |
6fc6879b JM |
1029 | if (data->last_eap_identity) { |
1030 | identity = data->last_eap_identity; | |
1031 | identity_len = data->last_eap_identity_len; | |
4df41339 HS |
1032 | } else if (data->pseudonym && |
1033 | !eap_sim_anonymous_username(data->pseudonym, | |
1034 | data->pseudonym_len)) { | |
6fc6879b JM |
1035 | identity = data->pseudonym; |
1036 | identity_len = data->pseudonym_len; | |
9e834fc6 JM |
1037 | } else { |
1038 | struct eap_peer_config *config; | |
1039 | ||
1040 | config = eap_get_config(sm); | |
1041 | if (config && config->imsi_identity) { | |
1042 | identity = config->imsi_identity; | |
1043 | identity_len = config->imsi_identity_len; | |
1044 | } else { | |
1045 | identity = eap_get_config_identity(sm, &identity_len); | |
1046 | } | |
1047 | } | |
6fc6879b JM |
1048 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " |
1049 | "derivation", identity, identity_len); | |
a9d1364c JM |
1050 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
1051 | eap_aka_prime_derive_keys(identity, identity_len, data->ik, | |
1052 | data->ck, data->k_encr, data->k_aut, | |
1053 | data->k_re, data->msk, data->emsk); | |
1054 | } else { | |
1055 | eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, | |
1056 | data->mk); | |
1057 | eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, | |
1058 | data->msk, data->emsk); | |
1059 | } | |
1060 | if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { | |
6fc6879b JM |
1061 | wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " |
1062 | "used invalid AT_MAC"); | |
1063 | return eap_aka_client_error(data, id, | |
1064 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1065 | } | |
1066 | ||
8b41e056 JM |
1067 | /* Old reauthentication identity must not be used anymore. In |
1068 | * other words, if no new identities are received, full | |
1069 | * authentication will be used on next reauthentication (using | |
1070 | * pseudonym identity or permanent identity). */ | |
e026159a | 1071 | eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); |
6fc6879b JM |
1072 | |
1073 | if (attr->encr_data) { | |
1074 | u8 *decrypted; | |
1075 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
1076 | attr->encr_data_len, attr->iv, | |
1077 | &eattr, 0); | |
1078 | if (decrypted == NULL) { | |
1079 | return eap_aka_client_error( | |
1080 | data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1081 | } | |
4ac384c5 | 1082 | eap_aka_learn_ids(sm, data, &eattr); |
6fc6879b JM |
1083 | os_free(decrypted); |
1084 | } | |
1085 | ||
1086 | if (data->result_ind && attr->result_ind) | |
1087 | data->use_result_ind = 1; | |
1088 | ||
79122f9f | 1089 | if (data->state != FAILURE) { |
6fc6879b JM |
1090 | eap_aka_state(data, data->use_result_ind ? |
1091 | RESULT_SUCCESS : SUCCESS); | |
1092 | } | |
1093 | ||
1094 | data->num_id_req = 0; | |
1095 | data->num_notification = 0; | |
1096 | /* RFC 4187 specifies that counter is initialized to one after | |
1097 | * fullauth, but initializing it to zero makes it easier to implement | |
1098 | * reauth verification. */ | |
1099 | data->counter = 0; | |
1100 | return eap_aka_response_challenge(data, id); | |
1101 | } | |
1102 | ||
1103 | ||
1104 | static int eap_aka_process_notification_reauth(struct eap_aka_data *data, | |
1105 | struct eap_sim_attrs *attr) | |
1106 | { | |
1107 | struct eap_sim_attrs eattr; | |
1108 | u8 *decrypted; | |
1109 | ||
1110 | if (attr->encr_data == NULL || attr->iv == NULL) { | |
1111 | wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " | |
1112 | "reauth did not include encrypted data"); | |
1113 | return -1; | |
1114 | } | |
1115 | ||
1116 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
1117 | attr->encr_data_len, attr->iv, &eattr, | |
1118 | 0); | |
1119 | if (decrypted == NULL) { | |
1120 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " | |
1121 | "data from notification message"); | |
1122 | return -1; | |
1123 | } | |
1124 | ||
1125 | if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { | |
1126 | wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " | |
1127 | "message does not match with counter in reauth " | |
1128 | "message"); | |
1129 | os_free(decrypted); | |
1130 | return -1; | |
1131 | } | |
1132 | ||
1133 | os_free(decrypted); | |
1134 | return 0; | |
1135 | } | |
1136 | ||
1137 | ||
1138 | static int eap_aka_process_notification_auth(struct eap_aka_data *data, | |
1139 | const struct wpabuf *reqData, | |
1140 | struct eap_sim_attrs *attr) | |
1141 | { | |
1142 | if (attr->mac == NULL) { | |
1143 | wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " | |
1144 | "Notification message"); | |
1145 | return -1; | |
1146 | } | |
1147 | ||
a9d1364c | 1148 | if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { |
6fc6879b JM |
1149 | wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " |
1150 | "used invalid AT_MAC"); | |
1151 | return -1; | |
1152 | } | |
1153 | ||
1154 | if (data->reauth && | |
1155 | eap_aka_process_notification_reauth(data, attr)) { | |
1156 | wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " | |
1157 | "message after reauth"); | |
1158 | return -1; | |
1159 | } | |
1160 | ||
1161 | return 0; | |
1162 | } | |
1163 | ||
1164 | ||
1165 | static struct wpabuf * eap_aka_process_notification( | |
1166 | struct eap_sm *sm, struct eap_aka_data *data, u8 id, | |
1167 | const struct wpabuf *reqData, struct eap_sim_attrs *attr) | |
1168 | { | |
1169 | wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); | |
1170 | if (data->num_notification > 0) { | |
1171 | wpa_printf(MSG_INFO, "EAP-AKA: too many notification " | |
1172 | "rounds (only one allowed)"); | |
1173 | return eap_aka_client_error(data, id, | |
1174 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1175 | } | |
1176 | data->num_notification++; | |
1177 | if (attr->notification == -1) { | |
1178 | wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " | |
1179 | "Notification message"); | |
1180 | return eap_aka_client_error(data, id, | |
1181 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1182 | } | |
1183 | ||
1184 | if ((attr->notification & 0x4000) == 0 && | |
1185 | eap_aka_process_notification_auth(data, reqData, attr)) { | |
1186 | return eap_aka_client_error(data, id, | |
1187 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1188 | } | |
1189 | ||
1190 | eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); | |
1191 | if (attr->notification >= 0 && attr->notification < 32768) { | |
45f7574d | 1192 | data->error_code = attr->notification; |
6fc6879b JM |
1193 | eap_aka_state(data, FAILURE); |
1194 | } else if (attr->notification == EAP_SIM_SUCCESS && | |
1195 | data->state == RESULT_SUCCESS) | |
1196 | eap_aka_state(data, SUCCESS); | |
1197 | return eap_aka_response_notification(data, id, attr->notification); | |
1198 | } | |
1199 | ||
1200 | ||
1201 | static struct wpabuf * eap_aka_process_reauthentication( | |
1202 | struct eap_sm *sm, struct eap_aka_data *data, u8 id, | |
1203 | const struct wpabuf *reqData, struct eap_sim_attrs *attr) | |
1204 | { | |
1205 | struct eap_sim_attrs eattr; | |
1206 | u8 *decrypted; | |
1207 | ||
1208 | wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); | |
1209 | ||
1210 | if (attr->checkcode && | |
1211 | eap_aka_verify_checkcode(data, attr->checkcode, | |
1212 | attr->checkcode_len)) { | |
1213 | wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " | |
1214 | "message"); | |
1215 | return eap_aka_client_error(data, id, | |
1216 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1217 | } | |
1218 | ||
1219 | if (data->reauth_id == NULL) { | |
1220 | wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " | |
1221 | "reauthentication, but no reauth_id available"); | |
1222 | return eap_aka_client_error(data, id, | |
1223 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1224 | } | |
1225 | ||
1226 | data->reauth = 1; | |
a9d1364c | 1227 | if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { |
6fc6879b JM |
1228 | wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " |
1229 | "did not have valid AT_MAC"); | |
1230 | return eap_aka_client_error(data, id, | |
1231 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1232 | } | |
1233 | ||
5eefa811 MS |
1234 | /* At this stage the received MAC has been verified. Use this MAC for |
1235 | * reauth Session-Id calculation if all other checks pass. | |
1236 | * The peer does not use the local MAC but the received MAC in deriving | |
1237 | * Session-Id. */ | |
1238 | os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); | |
1239 | wpa_hexdump(MSG_DEBUG, "EAP-AKA: Server MAC", | |
1240 | data->reauth_mac, EAP_SIM_MAC_LEN); | |
1241 | ||
6fc6879b JM |
1242 | if (attr->encr_data == NULL || attr->iv == NULL) { |
1243 | wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " | |
1244 | "message did not include encrypted data"); | |
1245 | return eap_aka_client_error(data, id, | |
1246 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1247 | } | |
1248 | ||
1249 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
1250 | attr->encr_data_len, attr->iv, &eattr, | |
1251 | 0); | |
1252 | if (decrypted == NULL) { | |
1253 | wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " | |
1254 | "data from reauthentication message"); | |
1255 | return eap_aka_client_error(data, id, | |
1256 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1257 | } | |
1258 | ||
1259 | if (eattr.nonce_s == NULL || eattr.counter < 0) { | |
1260 | wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", | |
1261 | !eattr.nonce_s ? " AT_NONCE_S" : "", | |
1262 | eattr.counter < 0 ? " AT_COUNTER" : ""); | |
1263 | os_free(decrypted); | |
1264 | return eap_aka_client_error(data, id, | |
1265 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1266 | } | |
1267 | ||
1268 | if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { | |
1269 | struct wpabuf *res; | |
1270 | wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " | |
1271 | "(%d <= %d)", eattr.counter, data->counter); | |
1272 | data->counter_too_small = eattr.counter; | |
1273 | ||
6fc6879b JM |
1274 | /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current |
1275 | * reauth_id must not be used to start a new reauthentication. | |
1276 | * However, since it was used in the last EAP-Response-Identity | |
1277 | * packet, it has to saved for the following fullauth to be | |
1278 | * used in MK derivation. */ | |
1279 | os_free(data->last_eap_identity); | |
1280 | data->last_eap_identity = data->reauth_id; | |
1281 | data->last_eap_identity_len = data->reauth_id_len; | |
1282 | data->reauth_id = NULL; | |
1283 | data->reauth_id_len = 0; | |
1284 | ||
1285 | res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); | |
1286 | os_free(decrypted); | |
1287 | ||
1288 | return res; | |
1289 | } | |
1290 | data->counter = eattr.counter; | |
1291 | ||
1292 | os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); | |
1293 | wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", | |
1294 | data->nonce_s, EAP_SIM_NONCE_S_LEN); | |
1295 | ||
a9d1364c JM |
1296 | if (data->eap_method == EAP_TYPE_AKA_PRIME) { |
1297 | eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, | |
1298 | data->reauth_id, | |
1299 | data->reauth_id_len, | |
1300 | data->nonce_s, | |
1301 | data->msk, data->emsk); | |
1302 | } else { | |
1303 | eap_sim_derive_keys_reauth(data->counter, data->reauth_id, | |
1304 | data->reauth_id_len, | |
1305 | data->nonce_s, data->mk, | |
1306 | data->msk, data->emsk); | |
1307 | } | |
e026159a | 1308 | eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); |
4ac384c5 | 1309 | eap_aka_learn_ids(sm, data, &eattr); |
6fc6879b JM |
1310 | |
1311 | if (data->result_ind && attr->result_ind) | |
1312 | data->use_result_ind = 1; | |
1313 | ||
79122f9f | 1314 | if (data->state != FAILURE) { |
6fc6879b JM |
1315 | eap_aka_state(data, data->use_result_ind ? |
1316 | RESULT_SUCCESS : SUCCESS); | |
1317 | } | |
1318 | ||
1319 | data->num_id_req = 0; | |
1320 | data->num_notification = 0; | |
1321 | if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { | |
1322 | wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " | |
1323 | "fast reauths performed - force fullauth"); | |
e026159a JM |
1324 | eap_aka_clear_identities(sm, data, |
1325 | CLEAR_REAUTH_ID | CLEAR_EAP_ID); | |
6fc6879b JM |
1326 | } |
1327 | os_free(decrypted); | |
1328 | return eap_aka_response_reauth(data, id, 0, data->nonce_s); | |
1329 | } | |
1330 | ||
1331 | ||
1332 | static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, | |
1333 | struct eap_method_ret *ret, | |
1334 | const struct wpabuf *reqData) | |
1335 | { | |
1336 | struct eap_aka_data *data = priv; | |
1337 | const struct eap_hdr *req; | |
1338 | u8 subtype, id; | |
1339 | struct wpabuf *res; | |
1340 | const u8 *pos; | |
1341 | struct eap_sim_attrs attr; | |
1342 | size_t len; | |
1343 | ||
1344 | wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); | |
1345 | if (eap_get_config_identity(sm, &len) == NULL) { | |
1346 | wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); | |
1347 | eap_sm_request_identity(sm); | |
1348 | ret->ignore = TRUE; | |
1349 | return NULL; | |
1350 | } | |
1351 | ||
a9d1364c JM |
1352 | pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, |
1353 | &len); | |
ff4a6d43 | 1354 | if (pos == NULL || len < 3) { |
6fc6879b JM |
1355 | ret->ignore = TRUE; |
1356 | return NULL; | |
1357 | } | |
1358 | req = wpabuf_head(reqData); | |
1359 | id = req->identifier; | |
1360 | len = be_to_host16(req->length); | |
1361 | ||
1362 | ret->ignore = FALSE; | |
1363 | ret->methodState = METHOD_MAY_CONT; | |
1364 | ret->decision = DECISION_FAIL; | |
1365 | ret->allowNotifications = TRUE; | |
1366 | ||
1367 | subtype = *pos++; | |
1368 | wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); | |
1369 | pos += 2; /* Reserved */ | |
1370 | ||
a9d1364c JM |
1371 | if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, |
1372 | data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, | |
6fc6879b JM |
1373 | 0)) { |
1374 | res = eap_aka_client_error(data, id, | |
1375 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1376 | goto done; | |
1377 | } | |
1378 | ||
1379 | switch (subtype) { | |
1380 | case EAP_AKA_SUBTYPE_IDENTITY: | |
1381 | res = eap_aka_process_identity(sm, data, id, reqData, &attr); | |
1382 | break; | |
1383 | case EAP_AKA_SUBTYPE_CHALLENGE: | |
1384 | res = eap_aka_process_challenge(sm, data, id, reqData, &attr); | |
1385 | break; | |
1386 | case EAP_AKA_SUBTYPE_NOTIFICATION: | |
1387 | res = eap_aka_process_notification(sm, data, id, reqData, | |
1388 | &attr); | |
1389 | break; | |
1390 | case EAP_AKA_SUBTYPE_REAUTHENTICATION: | |
1391 | res = eap_aka_process_reauthentication(sm, data, id, reqData, | |
1392 | &attr); | |
1393 | break; | |
1394 | case EAP_AKA_SUBTYPE_CLIENT_ERROR: | |
1395 | wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); | |
1396 | res = eap_aka_client_error(data, id, | |
1397 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1398 | break; | |
1399 | default: | |
1400 | wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); | |
1401 | res = eap_aka_client_error(data, id, | |
1402 | EAP_AKA_UNABLE_TO_PROCESS_PACKET); | |
1403 | break; | |
1404 | } | |
1405 | ||
1406 | done: | |
1407 | if (data->state == FAILURE) { | |
1408 | ret->decision = DECISION_FAIL; | |
1409 | ret->methodState = METHOD_DONE; | |
1410 | } else if (data->state == SUCCESS) { | |
1411 | ret->decision = data->use_result_ind ? | |
1412 | DECISION_UNCOND_SUCC : DECISION_COND_SUCC; | |
1413 | /* | |
1414 | * It is possible for the server to reply with AKA | |
1415 | * Notification, so we must allow the method to continue and | |
1416 | * not only accept EAP-Success at this point. | |
1417 | */ | |
1418 | ret->methodState = data->use_result_ind ? | |
1419 | METHOD_DONE : METHOD_MAY_CONT; | |
79122f9f | 1420 | } else if (data->state == RESULT_SUCCESS) |
6fc6879b JM |
1421 | ret->methodState = METHOD_CONT; |
1422 | ||
1423 | if (ret->methodState == METHOD_DONE) { | |
1424 | ret->allowNotifications = FALSE; | |
1425 | } | |
1426 | ||
1427 | return res; | |
1428 | } | |
1429 | ||
1430 | ||
1431 | static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) | |
1432 | { | |
1433 | struct eap_aka_data *data = priv; | |
1434 | return data->pseudonym || data->reauth_id; | |
1435 | } | |
1436 | ||
1437 | ||
1438 | static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) | |
1439 | { | |
1440 | struct eap_aka_data *data = priv; | |
e026159a | 1441 | eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); |
6fc6879b JM |
1442 | data->prev_id = -1; |
1443 | wpabuf_free(data->id_msgs); | |
1444 | data->id_msgs = NULL; | |
1445 | data->use_result_ind = 0; | |
2cfcd014 | 1446 | data->kdf_negotiation = 0; |
f534ee08 | 1447 | eap_aka_clear_keys(data, 1); |
6fc6879b JM |
1448 | } |
1449 | ||
1450 | ||
1451 | static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) | |
1452 | { | |
1453 | struct eap_aka_data *data = priv; | |
1454 | data->num_id_req = 0; | |
1455 | data->num_notification = 0; | |
1456 | eap_aka_state(data, CONTINUE); | |
1457 | return priv; | |
1458 | } | |
1459 | ||
1460 | ||
1461 | static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, | |
1462 | size_t *len) | |
1463 | { | |
1464 | struct eap_aka_data *data = priv; | |
1465 | ||
1466 | if (data->reauth_id) { | |
1467 | *len = data->reauth_id_len; | |
1468 | return data->reauth_id; | |
1469 | } | |
1470 | ||
1471 | if (data->pseudonym) { | |
1472 | *len = data->pseudonym_len; | |
1473 | return data->pseudonym; | |
1474 | } | |
1475 | ||
1476 | return NULL; | |
1477 | } | |
1478 | ||
1479 | ||
1480 | static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) | |
1481 | { | |
1482 | struct eap_aka_data *data = priv; | |
1483 | return data->state == SUCCESS; | |
1484 | } | |
1485 | ||
1486 | ||
1487 | static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
1488 | { | |
1489 | struct eap_aka_data *data = priv; | |
1490 | u8 *key; | |
1491 | ||
1492 | if (data->state != SUCCESS) | |
1493 | return NULL; | |
1494 | ||
a1f11e34 | 1495 | key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); |
6fc6879b JM |
1496 | if (key == NULL) |
1497 | return NULL; | |
1498 | ||
1499 | *len = EAP_SIM_KEYING_DATA_LEN; | |
6fc6879b JM |
1500 | |
1501 | return key; | |
1502 | } | |
1503 | ||
1504 | ||
9ca84274 JM |
1505 | static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) |
1506 | { | |
1507 | struct eap_aka_data *data = priv; | |
1508 | u8 *id; | |
1509 | ||
1510 | if (data->state != SUCCESS) | |
1511 | return NULL; | |
1512 | ||
5eefa811 MS |
1513 | if (!data->reauth) |
1514 | *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; | |
1515 | else | |
1516 | *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; | |
9ca84274 JM |
1517 | id = os_malloc(*len); |
1518 | if (id == NULL) | |
1519 | return NULL; | |
1520 | ||
1521 | id[0] = data->eap_method; | |
5eefa811 MS |
1522 | if (!data->reauth) { |
1523 | os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); | |
1524 | os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, | |
1525 | EAP_AKA_AUTN_LEN); | |
1526 | } else { | |
1527 | os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); | |
1528 | os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, | |
1529 | EAP_SIM_MAC_LEN); | |
1530 | } | |
9ca84274 JM |
1531 | wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); |
1532 | ||
1533 | return id; | |
1534 | } | |
1535 | ||
1536 | ||
6fc6879b JM |
1537 | static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) |
1538 | { | |
1539 | struct eap_aka_data *data = priv; | |
1540 | u8 *key; | |
1541 | ||
1542 | if (data->state != SUCCESS) | |
1543 | return NULL; | |
1544 | ||
a1f11e34 | 1545 | key = os_memdup(data->emsk, EAP_EMSK_LEN); |
6fc6879b JM |
1546 | if (key == NULL) |
1547 | return NULL; | |
1548 | ||
1549 | *len = EAP_EMSK_LEN; | |
6fc6879b JM |
1550 | |
1551 | return key; | |
1552 | } | |
1553 | ||
1554 | ||
45f7574d AE |
1555 | static int eap_aka_get_error_code(void *priv) |
1556 | { | |
1557 | struct eap_aka_data *data = priv; | |
1558 | int current_data_error; | |
1559 | ||
1560 | if (!data) | |
1561 | return NO_EAP_METHOD_ERROR; | |
1562 | ||
1563 | current_data_error = data->error_code; | |
1564 | ||
1565 | /* Now reset for next transaction */ | |
1566 | data->error_code = NO_EAP_METHOD_ERROR; | |
1567 | ||
1568 | return current_data_error; | |
1569 | } | |
1570 | ||
1571 | ||
6fc6879b JM |
1572 | int eap_peer_aka_register(void) |
1573 | { | |
1574 | struct eap_method *eap; | |
6fc6879b JM |
1575 | |
1576 | eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, | |
1577 | EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); | |
1578 | if (eap == NULL) | |
1579 | return -1; | |
1580 | ||
1581 | eap->init = eap_aka_init; | |
1582 | eap->deinit = eap_aka_deinit; | |
1583 | eap->process = eap_aka_process; | |
1584 | eap->isKeyAvailable = eap_aka_isKeyAvailable; | |
1585 | eap->getKey = eap_aka_getKey; | |
9ca84274 | 1586 | eap->getSessionId = eap_aka_get_session_id; |
6fc6879b JM |
1587 | eap->has_reauth_data = eap_aka_has_reauth_data; |
1588 | eap->deinit_for_reauth = eap_aka_deinit_for_reauth; | |
1589 | eap->init_for_reauth = eap_aka_init_for_reauth; | |
1590 | eap->get_identity = eap_aka_get_identity; | |
1591 | eap->get_emsk = eap_aka_get_emsk; | |
45f7574d | 1592 | eap->get_error_code = eap_aka_get_error_code; |
6fc6879b | 1593 | |
49a26bb3 | 1594 | return eap_peer_method_register(eap); |
6fc6879b | 1595 | } |
a9d1364c JM |
1596 | |
1597 | ||
1598 | #ifdef EAP_AKA_PRIME | |
1599 | int eap_peer_aka_prime_register(void) | |
1600 | { | |
1601 | struct eap_method *eap; | |
a9d1364c JM |
1602 | |
1603 | eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, | |
1604 | EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, | |
1605 | "AKA'"); | |
1606 | if (eap == NULL) | |
1607 | return -1; | |
1608 | ||
1609 | eap->init = eap_aka_prime_init; | |
1610 | eap->deinit = eap_aka_deinit; | |
1611 | eap->process = eap_aka_process; | |
1612 | eap->isKeyAvailable = eap_aka_isKeyAvailable; | |
1613 | eap->getKey = eap_aka_getKey; | |
9ca84274 | 1614 | eap->getSessionId = eap_aka_get_session_id; |
a9d1364c JM |
1615 | eap->has_reauth_data = eap_aka_has_reauth_data; |
1616 | eap->deinit_for_reauth = eap_aka_deinit_for_reauth; | |
1617 | eap->init_for_reauth = eap_aka_init_for_reauth; | |
1618 | eap->get_identity = eap_aka_get_identity; | |
1619 | eap->get_emsk = eap_aka_get_emsk; | |
45f7574d | 1620 | eap->get_error_code = eap_aka_get_error_code; |
a9d1364c | 1621 | |
49a26bb3 | 1622 | return eap_peer_method_register(eap); |
a9d1364c JM |
1623 | } |
1624 | #endif /* EAP_AKA_PRIME */ |