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