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