]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * EAP peer method: EAP-SIM (RFC 4186) | |
e026159a | 3 | * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
43df4cc2 JM |
12 | #include "pcsc_funcs.h" |
13 | #include "crypto/milenage.h" | |
3642c431 | 14 | #include "crypto/random.h" |
6fc6879b JM |
15 | #include "eap_peer/eap_i.h" |
16 | #include "eap_config.h" | |
6fc6879b JM |
17 | #include "eap_common/eap_sim_common.h" |
18 | ||
19 | ||
20 | struct eap_sim_data { | |
21 | u8 *ver_list; | |
22 | size_t ver_list_len; | |
23 | int selected_version; | |
24 | size_t min_num_chal, num_chal; | |
25 | ||
26 | u8 kc[3][EAP_SIM_KC_LEN]; | |
27 | u8 sres[3][EAP_SIM_SRES_LEN]; | |
28 | u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; | |
29 | u8 mk[EAP_SIM_MK_LEN]; | |
30 | u8 k_aut[EAP_SIM_K_AUT_LEN]; | |
31 | u8 k_encr[EAP_SIM_K_ENCR_LEN]; | |
32 | u8 msk[EAP_SIM_KEYING_DATA_LEN]; | |
33 | u8 emsk[EAP_EMSK_LEN]; | |
34 | u8 rand[3][GSM_RAND_LEN]; | |
1c16b257 | 35 | u8 reauth_mac[EAP_SIM_MAC_LEN]; |
6fc6879b JM |
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 *last_eap_identity; | |
45 | size_t last_eap_identity_len; | |
46 | enum { | |
07fe134d | 47 | CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE |
6fc6879b JM |
48 | } state; |
49 | int result_ind, use_result_ind; | |
9e2afe10 | 50 | int use_pseudonym; |
45f7574d | 51 | int error_code; |
6fc6879b JM |
52 | }; |
53 | ||
54 | ||
55 | #ifndef CONFIG_NO_STDOUT_DEBUG | |
56 | static const char * eap_sim_state_txt(int state) | |
57 | { | |
58 | switch (state) { | |
59 | case CONTINUE: | |
60 | return "CONTINUE"; | |
07fe134d JM |
61 | case START_DONE: |
62 | return "START_DONE"; | |
6fc6879b JM |
63 | case RESULT_SUCCESS: |
64 | return "RESULT_SUCCESS"; | |
6fc6879b JM |
65 | case SUCCESS: |
66 | return "SUCCESS"; | |
67 | case FAILURE: | |
68 | return "FAILURE"; | |
69 | default: | |
70 | return "?"; | |
71 | } | |
72 | } | |
73 | #endif /* CONFIG_NO_STDOUT_DEBUG */ | |
74 | ||
75 | ||
76 | static void eap_sim_state(struct eap_sim_data *data, int state) | |
77 | { | |
78 | wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", | |
79 | eap_sim_state_txt(data->state), | |
80 | eap_sim_state_txt(state)); | |
81 | data->state = state; | |
82 | } | |
83 | ||
84 | ||
85 | static void * eap_sim_init(struct eap_sm *sm) | |
86 | { | |
87 | struct eap_sim_data *data; | |
88 | struct eap_peer_config *config = eap_get_config(sm); | |
89 | ||
90 | data = os_zalloc(sizeof(*data)); | |
91 | if (data == NULL) | |
92 | return NULL; | |
93 | ||
3642c431 | 94 | if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { |
6fc6879b JM |
95 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " |
96 | "for NONCE_MT"); | |
97 | os_free(data); | |
98 | return NULL; | |
99 | } | |
100 | ||
45f7574d AE |
101 | /* Zero is a valid error code, so we need to initialize */ |
102 | data->error_code = NO_EAP_METHOD_ERROR; | |
103 | ||
6fc6879b JM |
104 | data->min_num_chal = 2; |
105 | if (config && config->phase1) { | |
106 | char *pos = os_strstr(config->phase1, "sim_min_num_chal="); | |
107 | if (pos) { | |
108 | data->min_num_chal = atoi(pos + 17); | |
109 | if (data->min_num_chal < 2 || data->min_num_chal > 3) { | |
110 | wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " | |
111 | "sim_min_num_chal configuration " | |
112 | "(%lu, expected 2 or 3)", | |
113 | (unsigned long) data->min_num_chal); | |
114 | os_free(data); | |
115 | return NULL; | |
116 | } | |
117 | wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " | |
118 | "challenges to %lu", | |
119 | (unsigned long) data->min_num_chal); | |
120 | } | |
121 | ||
122 | data->result_ind = os_strstr(config->phase1, "result_ind=1") != | |
123 | NULL; | |
124 | } | |
125 | ||
9e2afe10 PS |
126 | data->use_pseudonym = !sm->init_phase2; |
127 | if (config && config->anonymous_identity && data->use_pseudonym) { | |
e026159a JM |
128 | data->pseudonym = os_malloc(config->anonymous_identity_len); |
129 | if (data->pseudonym) { | |
130 | os_memcpy(data->pseudonym, config->anonymous_identity, | |
131 | config->anonymous_identity_len); | |
132 | data->pseudonym_len = config->anonymous_identity_len; | |
133 | } | |
134 | } | |
135 | ||
6fc6879b JM |
136 | eap_sim_state(data, CONTINUE); |
137 | ||
138 | return data; | |
139 | } | |
140 | ||
141 | ||
f534ee08 JM |
142 | static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth) |
143 | { | |
144 | if (!reauth) { | |
145 | os_memset(data->mk, 0, EAP_SIM_MK_LEN); | |
146 | os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN); | |
147 | os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN); | |
148 | } | |
149 | os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN); | |
150 | os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN); | |
151 | os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN); | |
152 | os_memset(data->emsk, 0, EAP_EMSK_LEN); | |
153 | } | |
154 | ||
155 | ||
6fc6879b JM |
156 | static void eap_sim_deinit(struct eap_sm *sm, void *priv) |
157 | { | |
158 | struct eap_sim_data *data = priv; | |
159 | if (data) { | |
160 | os_free(data->ver_list); | |
161 | os_free(data->pseudonym); | |
162 | os_free(data->reauth_id); | |
163 | os_free(data->last_eap_identity); | |
f534ee08 | 164 | eap_sim_clear_keys(data, 0); |
6fc6879b JM |
165 | os_free(data); |
166 | } | |
167 | } | |
168 | ||
169 | ||
569ccf71 JM |
170 | static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) |
171 | { | |
172 | char req[200], *pos, *end; | |
173 | size_t i; | |
174 | ||
175 | wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); | |
176 | pos = req; | |
177 | end = pos + sizeof(req); | |
178 | pos += os_snprintf(pos, end - pos, "GSM-AUTH"); | |
179 | for (i = 0; i < data->num_chal; i++) { | |
180 | pos += os_snprintf(pos, end - pos, ":"); | |
181 | pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], | |
182 | GSM_RAND_LEN); | |
183 | } | |
184 | ||
185 | eap_sm_request_sim(sm, req); | |
186 | return 1; | |
187 | } | |
188 | ||
189 | ||
190 | static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, | |
191 | struct eap_peer_config *conf) | |
192 | { | |
193 | char *resp, *pos; | |
194 | size_t i; | |
195 | ||
196 | wpa_printf(MSG_DEBUG, | |
197 | "EAP-SIM: Use result from external SIM processing"); | |
198 | ||
199 | resp = conf->external_sim_resp; | |
200 | conf->external_sim_resp = NULL; | |
201 | ||
202 | if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { | |
203 | wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); | |
204 | os_free(resp); | |
205 | return -1; | |
206 | } | |
207 | ||
208 | pos = resp + 9; | |
209 | for (i = 0; i < data->num_chal; i++) { | |
210 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", | |
211 | data->rand[i], GSM_RAND_LEN); | |
212 | ||
213 | if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) | |
214 | goto invalid; | |
215 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", | |
216 | data->kc[i], EAP_SIM_KC_LEN); | |
217 | pos += EAP_SIM_KC_LEN * 2; | |
218 | if (*pos != ':') | |
219 | goto invalid; | |
220 | pos++; | |
221 | ||
222 | if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) | |
223 | goto invalid; | |
224 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", | |
225 | data->sres[i], EAP_SIM_SRES_LEN); | |
226 | pos += EAP_SIM_SRES_LEN * 2; | |
227 | if (i + 1 < data->num_chal) { | |
228 | if (*pos != ':') | |
229 | goto invalid; | |
230 | pos++; | |
231 | } | |
232 | } | |
233 | ||
234 | os_free(resp); | |
235 | return 0; | |
236 | ||
237 | invalid: | |
238 | wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); | |
239 | os_free(resp); | |
240 | return -1; | |
241 | } | |
242 | ||
243 | ||
6fc6879b JM |
244 | static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) |
245 | { | |
81eec387 JM |
246 | struct eap_peer_config *conf; |
247 | ||
6fc6879b | 248 | wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); |
81eec387 JM |
249 | |
250 | conf = eap_get_config(sm); | |
251 | if (conf == NULL) | |
6fc6879b | 252 | return -1; |
569ccf71 JM |
253 | |
254 | if (sm->external_sim) { | |
255 | if (conf->external_sim_resp) | |
256 | return eap_sim_ext_sim_result(sm, data, conf); | |
257 | else | |
258 | return eap_sim_ext_sim_req(sm, data); | |
259 | } | |
260 | ||
269f9d5d | 261 | #ifdef PCSC_FUNCS |
81eec387 JM |
262 | if (conf->pcsc) { |
263 | if (scard_gsm_auth(sm->scard_ctx, data->rand[0], | |
264 | data->sres[0], data->kc[0]) || | |
265 | scard_gsm_auth(sm->scard_ctx, data->rand[1], | |
266 | data->sres[1], data->kc[1]) || | |
267 | (data->num_chal > 2 && | |
268 | scard_gsm_auth(sm->scard_ctx, data->rand[2], | |
269 | data->sres[2], data->kc[2]))) { | |
270 | wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM " | |
271 | "authentication could not be completed"); | |
272 | return -1; | |
273 | } | |
274 | return 0; | |
275 | } | |
269f9d5d | 276 | #endif /* PCSC_FUNCS */ |
81eec387 JM |
277 | |
278 | #ifdef CONFIG_SIM_SIMULATOR | |
279 | if (conf->password) { | |
280 | u8 opc[16], k[16]; | |
281 | const char *pos; | |
a532c108 | 282 | size_t i; |
81eec387 JM |
283 | wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage " |
284 | "implementation for authentication"); | |
285 | if (conf->password_len < 65) { | |
286 | wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage " | |
287 | "password"); | |
288 | return -1; | |
289 | } | |
290 | pos = (const char *) conf->password; | |
291 | if (hexstr2bin(pos, k, 16)) | |
292 | return -1; | |
293 | pos += 32; | |
294 | if (*pos != ':') | |
295 | return -1; | |
296 | pos++; | |
297 | ||
298 | if (hexstr2bin(pos, opc, 16)) | |
299 | return -1; | |
300 | ||
a532c108 JM |
301 | for (i = 0; i < data->num_chal; i++) { |
302 | if (gsm_milenage(opc, k, data->rand[i], | |
303 | data->sres[i], data->kc[i])) { | |
304 | wpa_printf(MSG_DEBUG, "EAP-SIM: " | |
305 | "GSM-Milenage authentication " | |
306 | "could not be completed"); | |
307 | return -1; | |
308 | } | |
309 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", | |
310 | data->rand[i], GSM_RAND_LEN); | |
311 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", | |
312 | data->sres[i], EAP_SIM_SRES_LEN); | |
313 | wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", | |
314 | data->kc[i], EAP_SIM_KC_LEN); | |
81eec387 JM |
315 | } |
316 | return 0; | |
6fc6879b | 317 | } |
81eec387 JM |
318 | #endif /* CONFIG_SIM_SIMULATOR */ |
319 | ||
320 | #ifdef CONFIG_SIM_HARDCODED | |
6fc6879b JM |
321 | /* These hardcoded Kc and SRES values are used for testing. RAND to |
322 | * KC/SREC mapping is very bogus as far as real authentication is | |
323 | * concerned, but it is quite useful for cases where the AS is rotating | |
324 | * the order of pre-configured values. */ | |
325 | { | |
326 | size_t i; | |
81eec387 JM |
327 | |
328 | wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES " | |
329 | "values for testing"); | |
330 | ||
6fc6879b JM |
331 | for (i = 0; i < data->num_chal; i++) { |
332 | if (data->rand[i][0] == 0xaa) { | |
333 | os_memcpy(data->kc[i], | |
334 | "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", | |
335 | EAP_SIM_KC_LEN); | |
336 | os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", | |
337 | EAP_SIM_SRES_LEN); | |
338 | } else if (data->rand[i][0] == 0xbb) { | |
339 | os_memcpy(data->kc[i], | |
340 | "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", | |
341 | EAP_SIM_KC_LEN); | |
342 | os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", | |
343 | EAP_SIM_SRES_LEN); | |
344 | } else { | |
345 | os_memcpy(data->kc[i], | |
346 | "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", | |
347 | EAP_SIM_KC_LEN); | |
348 | os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", | |
349 | EAP_SIM_SRES_LEN); | |
350 | } | |
351 | } | |
352 | } | |
81eec387 | 353 | |
6fc6879b | 354 | return 0; |
81eec387 JM |
355 | |
356 | #else /* CONFIG_SIM_HARDCODED */ | |
357 | ||
358 | wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm " | |
359 | "enabled"); | |
360 | return -1; | |
361 | ||
362 | #endif /* CONFIG_SIM_HARDCODED */ | |
6fc6879b JM |
363 | } |
364 | ||
365 | ||
366 | static int eap_sim_supported_ver(int version) | |
367 | { | |
368 | return version == EAP_SIM_VERSION; | |
369 | } | |
370 | ||
371 | ||
372 | #define CLEAR_PSEUDONYM 0x01 | |
373 | #define CLEAR_REAUTH_ID 0x02 | |
374 | #define CLEAR_EAP_ID 0x04 | |
375 | ||
e026159a JM |
376 | static void eap_sim_clear_identities(struct eap_sm *sm, |
377 | struct eap_sim_data *data, int id) | |
6fc6879b | 378 | { |
1037235c SB |
379 | if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { |
380 | wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); | |
6fc6879b JM |
381 | os_free(data->pseudonym); |
382 | data->pseudonym = NULL; | |
383 | data->pseudonym_len = 0; | |
9e2afe10 PS |
384 | if (data->use_pseudonym) |
385 | eap_set_anon_id(sm, NULL, 0); | |
6fc6879b | 386 | } |
1037235c SB |
387 | if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { |
388 | wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); | |
6fc6879b JM |
389 | os_free(data->reauth_id); |
390 | data->reauth_id = NULL; | |
391 | data->reauth_id_len = 0; | |
392 | } | |
1037235c SB |
393 | if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { |
394 | wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id"); | |
6fc6879b JM |
395 | os_free(data->last_eap_identity); |
396 | data->last_eap_identity = NULL; | |
397 | data->last_eap_identity_len = 0; | |
398 | } | |
399 | } | |
400 | ||
401 | ||
a6689be8 | 402 | static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, |
6fc6879b JM |
403 | struct eap_sim_attrs *attr) |
404 | { | |
405 | if (attr->next_pseudonym) { | |
a6689be8 SB |
406 | const u8 *identity = NULL; |
407 | size_t identity_len = 0; | |
408 | const u8 *realm = NULL; | |
409 | size_t realm_len = 0; | |
410 | ||
411 | wpa_hexdump_ascii(MSG_DEBUG, | |
412 | "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", | |
413 | attr->next_pseudonym, | |
414 | attr->next_pseudonym_len); | |
6fc6879b | 415 | os_free(data->pseudonym); |
a6689be8 SB |
416 | /* Look for the realm of the permanent identity */ |
417 | identity = eap_get_config_identity(sm, &identity_len); | |
418 | if (identity) { | |
419 | for (realm = identity, realm_len = identity_len; | |
420 | realm_len > 0; realm_len--, realm++) { | |
421 | if (*realm == '@') | |
422 | break; | |
423 | } | |
424 | } | |
425 | data->pseudonym = os_malloc(attr->next_pseudonym_len + | |
426 | realm_len); | |
6fc6879b JM |
427 | if (data->pseudonym == NULL) { |
428 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " | |
429 | "next pseudonym"); | |
a6689be8 | 430 | data->pseudonym_len = 0; |
6fc6879b JM |
431 | return -1; |
432 | } | |
433 | os_memcpy(data->pseudonym, attr->next_pseudonym, | |
434 | attr->next_pseudonym_len); | |
a6689be8 SB |
435 | if (realm_len) { |
436 | os_memcpy(data->pseudonym + attr->next_pseudonym_len, | |
437 | realm, realm_len); | |
438 | } | |
439 | data->pseudonym_len = attr->next_pseudonym_len + realm_len; | |
9e2afe10 PS |
440 | if (data->use_pseudonym) |
441 | eap_set_anon_id(sm, data->pseudonym, | |
442 | data->pseudonym_len); | |
6fc6879b JM |
443 | } |
444 | ||
445 | if (attr->next_reauth_id) { | |
446 | os_free(data->reauth_id); | |
a1f11e34 JB |
447 | data->reauth_id = os_memdup(attr->next_reauth_id, |
448 | attr->next_reauth_id_len); | |
6fc6879b JM |
449 | if (data->reauth_id == NULL) { |
450 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " | |
451 | "next reauth_id"); | |
a6689be8 | 452 | data->reauth_id_len = 0; |
6fc6879b JM |
453 | return -1; |
454 | } | |
6fc6879b JM |
455 | data->reauth_id_len = attr->next_reauth_id_len; |
456 | wpa_hexdump_ascii(MSG_DEBUG, | |
457 | "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", | |
458 | data->reauth_id, | |
459 | data->reauth_id_len); | |
460 | } | |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
465 | ||
466 | static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, | |
467 | int err) | |
468 | { | |
469 | struct eap_sim_msg *msg; | |
470 | ||
471 | eap_sim_state(data, FAILURE); | |
472 | data->num_id_req = 0; | |
473 | data->num_notification = 0; | |
474 | ||
93434989 JM |
475 | wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", |
476 | err); | |
6fc6879b JM |
477 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, |
478 | EAP_SIM_SUBTYPE_CLIENT_ERROR); | |
479 | eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); | |
b2b8a4cb | 480 | return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); |
6fc6879b JM |
481 | } |
482 | ||
483 | ||
484 | static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, | |
485 | struct eap_sim_data *data, u8 id, | |
486 | enum eap_sim_id_req id_req) | |
487 | { | |
488 | const u8 *identity = NULL; | |
489 | size_t identity_len = 0; | |
490 | struct eap_sim_msg *msg; | |
07fe134d | 491 | struct wpabuf *resp; |
6fc6879b JM |
492 | |
493 | data->reauth = 0; | |
494 | if (id_req == ANY_ID && data->reauth_id) { | |
495 | identity = data->reauth_id; | |
496 | identity_len = data->reauth_id_len; | |
497 | data->reauth = 1; | |
498 | } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && | |
4df41339 HS |
499 | data->pseudonym && |
500 | !eap_sim_anonymous_username(data->pseudonym, | |
501 | data->pseudonym_len)) { | |
6fc6879b JM |
502 | identity = data->pseudonym; |
503 | identity_len = data->pseudonym_len; | |
e026159a | 504 | eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); |
6fc6879b JM |
505 | } else if (id_req != NO_ID_REQ) { |
506 | identity = eap_get_config_identity(sm, &identity_len); | |
507 | if (identity) { | |
c1b23652 JM |
508 | int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID; |
509 | ||
510 | if (data->pseudonym && | |
511 | eap_sim_anonymous_username(data->pseudonym, | |
512 | data->pseudonym_len)) | |
513 | ids &= ~CLEAR_PSEUDONYM; | |
514 | eap_sim_clear_identities(sm, data, ids); | |
6fc6879b JM |
515 | } |
516 | } | |
517 | if (id_req != NO_ID_REQ) | |
e026159a | 518 | eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); |
6fc6879b JM |
519 | |
520 | wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); | |
521 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, | |
522 | EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); | |
523 | if (!data->reauth) { | |
524 | wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", | |
525 | data->nonce_mt, EAP_SIM_NONCE_MT_LEN); | |
526 | eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, | |
527 | data->nonce_mt, EAP_SIM_NONCE_MT_LEN); | |
528 | wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", | |
529 | data->selected_version); | |
530 | eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, | |
531 | data->selected_version, NULL, 0); | |
532 | } | |
533 | ||
534 | if (identity) { | |
535 | wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", | |
536 | identity, identity_len); | |
537 | eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, | |
538 | identity, identity_len); | |
539 | } | |
540 | ||
07fe134d JM |
541 | resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); |
542 | if (resp) | |
543 | eap_sim_state(data, START_DONE); | |
544 | return resp; | |
6fc6879b JM |
545 | } |
546 | ||
547 | ||
548 | static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, | |
549 | u8 id) | |
550 | { | |
551 | struct eap_sim_msg *msg; | |
552 | ||
553 | wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id); | |
554 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, | |
555 | EAP_SIM_SUBTYPE_CHALLENGE); | |
556 | if (data->use_result_ind) { | |
557 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
558 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
559 | } | |
560 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
561 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
b2b8a4cb JM |
562 | return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, |
563 | (u8 *) data->sres, | |
6fc6879b JM |
564 | data->num_chal * EAP_SIM_SRES_LEN); |
565 | } | |
566 | ||
567 | ||
568 | static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, | |
5d65ca51 JM |
569 | u8 id, int counter_too_small, |
570 | const u8 *nonce_s) | |
6fc6879b JM |
571 | { |
572 | struct eap_sim_msg *msg; | |
573 | unsigned int counter; | |
574 | ||
575 | wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", | |
576 | id); | |
577 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, | |
578 | EAP_SIM_SUBTYPE_REAUTHENTICATION); | |
579 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
580 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
581 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); | |
582 | ||
583 | if (counter_too_small) { | |
584 | wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); | |
585 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); | |
586 | counter = data->counter_too_small; | |
587 | } else | |
588 | counter = data->counter; | |
589 | ||
590 | wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); | |
591 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); | |
592 | ||
593 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { | |
594 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " | |
595 | "AT_ENCR_DATA"); | |
596 | eap_sim_msg_free(msg); | |
597 | return NULL; | |
598 | } | |
599 | if (data->use_result_ind) { | |
600 | wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); | |
601 | eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); | |
602 | } | |
603 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
604 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
b2b8a4cb | 605 | return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s, |
6fc6879b JM |
606 | EAP_SIM_NONCE_S_LEN); |
607 | } | |
608 | ||
609 | ||
610 | static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, | |
611 | u8 id, u16 notification) | |
612 | { | |
613 | struct eap_sim_msg *msg; | |
614 | u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; | |
615 | ||
616 | wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); | |
617 | msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, | |
618 | EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); | |
6fc6879b JM |
619 | if (k_aut && data->reauth) { |
620 | wpa_printf(MSG_DEBUG, " AT_IV"); | |
621 | wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); | |
622 | eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, | |
623 | EAP_SIM_AT_ENCR_DATA); | |
624 | wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); | |
625 | eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, | |
626 | NULL, 0); | |
627 | if (eap_sim_msg_add_encr_end(msg, data->k_encr, | |
628 | EAP_SIM_AT_PADDING)) { | |
629 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " | |
630 | "AT_ENCR_DATA"); | |
631 | eap_sim_msg_free(msg); | |
632 | return NULL; | |
633 | } | |
634 | } | |
635 | if (k_aut) { | |
636 | wpa_printf(MSG_DEBUG, " AT_MAC"); | |
637 | eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); | |
638 | } | |
b2b8a4cb | 639 | return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0); |
6fc6879b JM |
640 | } |
641 | ||
642 | ||
643 | static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, | |
644 | struct eap_sim_data *data, u8 id, | |
645 | struct eap_sim_attrs *attr) | |
646 | { | |
647 | int selected_version = -1, id_error; | |
648 | size_t i; | |
649 | u8 *pos; | |
650 | ||
651 | wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); | |
652 | if (attr->version_list == NULL) { | |
653 | wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " | |
654 | "SIM/Start"); | |
655 | return eap_sim_client_error(data, id, | |
656 | EAP_SIM_UNSUPPORTED_VERSION); | |
657 | } | |
658 | ||
659 | os_free(data->ver_list); | |
a1f11e34 | 660 | data->ver_list = os_memdup(attr->version_list, attr->version_list_len); |
6fc6879b JM |
661 | if (data->ver_list == NULL) { |
662 | wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " | |
663 | "memory for version list"); | |
664 | return eap_sim_client_error(data, id, | |
665 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
666 | } | |
6fc6879b JM |
667 | data->ver_list_len = attr->version_list_len; |
668 | pos = data->ver_list; | |
669 | for (i = 0; i < data->ver_list_len / 2; i++) { | |
670 | int ver = pos[0] * 256 + pos[1]; | |
671 | pos += 2; | |
672 | if (eap_sim_supported_ver(ver)) { | |
673 | selected_version = ver; | |
674 | break; | |
675 | } | |
676 | } | |
677 | if (selected_version < 0) { | |
678 | wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " | |
679 | "version"); | |
680 | return eap_sim_client_error(data, id, | |
681 | EAP_SIM_UNSUPPORTED_VERSION); | |
682 | } | |
683 | wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", | |
684 | selected_version); | |
685 | data->selected_version = selected_version; | |
686 | ||
687 | id_error = 0; | |
688 | switch (attr->id_req) { | |
689 | case NO_ID_REQ: | |
690 | break; | |
691 | case ANY_ID: | |
692 | if (data->num_id_req > 0) | |
693 | id_error++; | |
694 | data->num_id_req++; | |
695 | break; | |
696 | case FULLAUTH_ID: | |
697 | if (data->num_id_req > 1) | |
698 | id_error++; | |
699 | data->num_id_req++; | |
700 | break; | |
701 | case PERMANENT_ID: | |
702 | if (data->num_id_req > 2) | |
703 | id_error++; | |
704 | data->num_id_req++; | |
705 | break; | |
706 | } | |
707 | if (id_error) { | |
708 | wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " | |
709 | "used within one authentication"); | |
710 | return eap_sim_client_error(data, id, | |
711 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
712 | } | |
713 | ||
714 | return eap_sim_response_start(sm, data, id, attr->id_req); | |
715 | } | |
716 | ||
717 | ||
718 | static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, | |
719 | struct eap_sim_data *data, | |
720 | u8 id, | |
721 | const struct wpabuf *reqData, | |
722 | struct eap_sim_attrs *attr) | |
723 | { | |
724 | const u8 *identity; | |
725 | size_t identity_len; | |
726 | struct eap_sim_attrs eattr; | |
569ccf71 | 727 | int res; |
6fc6879b JM |
728 | |
729 | wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); | |
07fe134d JM |
730 | if (data->state != START_DONE) { |
731 | wpa_printf(MSG_DEBUG, | |
732 | "EAP-SIM: Unexpected Challenge in state %s", | |
733 | eap_sim_state_txt(data->state)); | |
734 | return eap_sim_client_error(data, id, | |
735 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
736 | } | |
6fc6879b JM |
737 | data->reauth = 0; |
738 | if (!attr->mac || !attr->rand) { | |
739 | wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " | |
740 | "did not include%s%s", | |
741 | !attr->mac ? " AT_MAC" : "", | |
742 | !attr->rand ? " AT_RAND" : ""); | |
743 | return eap_sim_client_error(data, id, | |
744 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
745 | } | |
746 | ||
747 | wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", | |
748 | (unsigned long) attr->num_chal); | |
749 | if (attr->num_chal < data->min_num_chal) { | |
750 | wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " | |
751 | "challenges (%lu)", (unsigned long) attr->num_chal); | |
752 | return eap_sim_client_error(data, id, | |
753 | EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); | |
754 | } | |
755 | if (attr->num_chal > 3) { | |
756 | wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " | |
757 | "(%lu)", (unsigned long) attr->num_chal); | |
758 | return eap_sim_client_error(data, id, | |
759 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
760 | } | |
761 | ||
762 | /* Verify that RANDs are different */ | |
763 | if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN, | |
764 | GSM_RAND_LEN) == 0 || | |
765 | (attr->num_chal > 2 && | |
766 | (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, | |
767 | GSM_RAND_LEN) == 0 || | |
768 | os_memcmp(attr->rand + GSM_RAND_LEN, | |
769 | attr->rand + 2 * GSM_RAND_LEN, | |
770 | GSM_RAND_LEN) == 0))) { | |
771 | wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); | |
772 | return eap_sim_client_error(data, id, | |
773 | EAP_SIM_RAND_NOT_FRESH); | |
774 | } | |
775 | ||
776 | os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); | |
777 | data->num_chal = attr->num_chal; | |
569ccf71 JM |
778 | |
779 | res = eap_sim_gsm_auth(sm, data); | |
780 | if (res > 0) { | |
781 | wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); | |
782 | return NULL; | |
783 | } | |
784 | if (res) { | |
6fc6879b JM |
785 | wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); |
786 | return eap_sim_client_error(data, id, | |
787 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
788 | } | |
789 | if (data->last_eap_identity) { | |
790 | identity = data->last_eap_identity; | |
791 | identity_len = data->last_eap_identity_len; | |
4df41339 HS |
792 | } else if (data->pseudonym && |
793 | !eap_sim_anonymous_username(data->pseudonym, | |
794 | data->pseudonym_len)) { | |
6fc6879b JM |
795 | identity = data->pseudonym; |
796 | identity_len = data->pseudonym_len; | |
9e834fc6 JM |
797 | } else { |
798 | struct eap_peer_config *config; | |
799 | ||
800 | config = eap_get_config(sm); | |
801 | if (config && config->imsi_identity) { | |
802 | identity = config->imsi_identity; | |
803 | identity_len = config->imsi_identity_len; | |
804 | } else { | |
805 | identity = eap_get_config_identity(sm, &identity_len); | |
806 | } | |
807 | } | |
6fc6879b JM |
808 | wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " |
809 | "derivation", identity, identity_len); | |
810 | eap_sim_derive_mk(identity, identity_len, data->nonce_mt, | |
811 | data->selected_version, data->ver_list, | |
812 | data->ver_list_len, data->num_chal, | |
813 | (const u8 *) data->kc, data->mk); | |
814 | eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, | |
815 | data->emsk); | |
816 | if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt, | |
817 | EAP_SIM_NONCE_MT_LEN)) { | |
818 | wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " | |
819 | "used invalid AT_MAC"); | |
23ddc7b8 JM |
820 | #ifdef TEST_FUZZ |
821 | wpa_printf(MSG_INFO, | |
822 | "TEST: Ignore AT_MAC mismatch for fuzz testing"); | |
823 | #else /* TEST_FUZZ */ | |
6fc6879b JM |
824 | return eap_sim_client_error(data, id, |
825 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
23ddc7b8 | 826 | #endif /* TEST_FUZZ */ |
6fc6879b JM |
827 | } |
828 | ||
a9f40ae7 SB |
829 | /* Old reauthentication identity must not be used anymore. In |
830 | * other words, if no new reauth identity is received, full | |
831 | * authentication will be used on next reauthentication (using | |
832 | * pseudonym identity or permanent identity). */ | |
e026159a | 833 | eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); |
6fc6879b JM |
834 | |
835 | if (attr->encr_data) { | |
836 | u8 *decrypted; | |
837 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
838 | attr->encr_data_len, attr->iv, | |
839 | &eattr, 0); | |
840 | if (decrypted == NULL) { | |
841 | return eap_sim_client_error( | |
842 | data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
843 | } | |
a6689be8 | 844 | eap_sim_learn_ids(sm, data, &eattr); |
6fc6879b JM |
845 | os_free(decrypted); |
846 | } | |
847 | ||
848 | if (data->result_ind && attr->result_ind) | |
849 | data->use_result_ind = 1; | |
850 | ||
79122f9f | 851 | if (data->state != FAILURE) { |
6fc6879b JM |
852 | eap_sim_state(data, data->use_result_ind ? |
853 | RESULT_SUCCESS : SUCCESS); | |
854 | } | |
855 | ||
856 | data->num_id_req = 0; | |
857 | data->num_notification = 0; | |
858 | /* RFC 4186 specifies that counter is initialized to one after | |
859 | * fullauth, but initializing it to zero makes it easier to implement | |
860 | * reauth verification. */ | |
861 | data->counter = 0; | |
862 | return eap_sim_response_challenge(data, id); | |
863 | } | |
864 | ||
865 | ||
866 | static int eap_sim_process_notification_reauth(struct eap_sim_data *data, | |
867 | struct eap_sim_attrs *attr) | |
868 | { | |
869 | struct eap_sim_attrs eattr; | |
870 | u8 *decrypted; | |
871 | ||
872 | if (attr->encr_data == NULL || attr->iv == NULL) { | |
873 | wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " | |
874 | "reauth did not include encrypted data"); | |
875 | return -1; | |
876 | } | |
877 | ||
878 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
879 | attr->encr_data_len, attr->iv, &eattr, | |
880 | 0); | |
881 | if (decrypted == NULL) { | |
882 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " | |
883 | "data from notification message"); | |
884 | return -1; | |
885 | } | |
886 | ||
887 | if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { | |
888 | wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " | |
889 | "message does not match with counter in reauth " | |
890 | "message"); | |
891 | os_free(decrypted); | |
892 | return -1; | |
893 | } | |
894 | ||
895 | os_free(decrypted); | |
896 | return 0; | |
897 | } | |
898 | ||
899 | ||
900 | static int eap_sim_process_notification_auth(struct eap_sim_data *data, | |
901 | const struct wpabuf *reqData, | |
902 | struct eap_sim_attrs *attr) | |
903 | { | |
904 | if (attr->mac == NULL) { | |
905 | wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " | |
906 | "Notification message"); | |
907 | return -1; | |
908 | } | |
909 | ||
910 | if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) | |
911 | { | |
912 | wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " | |
913 | "used invalid AT_MAC"); | |
914 | return -1; | |
915 | } | |
916 | ||
917 | if (data->reauth && | |
918 | eap_sim_process_notification_reauth(data, attr)) { | |
919 | wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " | |
920 | "message after reauth"); | |
921 | return -1; | |
922 | } | |
923 | ||
924 | return 0; | |
925 | } | |
926 | ||
927 | ||
928 | static struct wpabuf * eap_sim_process_notification( | |
929 | struct eap_sm *sm, struct eap_sim_data *data, u8 id, | |
930 | const struct wpabuf *reqData, struct eap_sim_attrs *attr) | |
931 | { | |
932 | wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); | |
933 | if (data->num_notification > 0) { | |
934 | wpa_printf(MSG_INFO, "EAP-SIM: too many notification " | |
935 | "rounds (only one allowed)"); | |
936 | return eap_sim_client_error(data, id, | |
937 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
938 | } | |
939 | data->num_notification++; | |
940 | if (attr->notification == -1) { | |
941 | wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " | |
942 | "Notification message"); | |
943 | return eap_sim_client_error(data, id, | |
944 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
945 | } | |
946 | ||
947 | if ((attr->notification & 0x4000) == 0 && | |
948 | eap_sim_process_notification_auth(data, reqData, attr)) { | |
949 | return eap_sim_client_error(data, id, | |
950 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
951 | } | |
952 | ||
953 | eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); | |
954 | if (attr->notification >= 0 && attr->notification < 32768) { | |
45f7574d | 955 | data->error_code = attr->notification; |
6fc6879b JM |
956 | eap_sim_state(data, FAILURE); |
957 | } else if (attr->notification == EAP_SIM_SUCCESS && | |
958 | data->state == RESULT_SUCCESS) | |
959 | eap_sim_state(data, SUCCESS); | |
960 | return eap_sim_response_notification(data, id, attr->notification); | |
961 | } | |
962 | ||
963 | ||
964 | static struct wpabuf * eap_sim_process_reauthentication( | |
965 | struct eap_sm *sm, struct eap_sim_data *data, u8 id, | |
966 | const struct wpabuf *reqData, struct eap_sim_attrs *attr) | |
967 | { | |
968 | struct eap_sim_attrs eattr; | |
969 | u8 *decrypted; | |
970 | ||
971 | wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); | |
972 | ||
973 | if (data->reauth_id == NULL) { | |
974 | wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " | |
975 | "reauthentication, but no reauth_id available"); | |
976 | return eap_sim_client_error(data, id, | |
977 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
978 | } | |
979 | ||
980 | data->reauth = 1; | |
981 | if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) | |
982 | { | |
983 | wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " | |
984 | "did not have valid AT_MAC"); | |
23ddc7b8 JM |
985 | #ifdef TEST_FUZZ |
986 | wpa_printf(MSG_INFO, | |
987 | "TEST: Ignore AT_MAC mismatch for fuzz testing"); | |
988 | #else /* TEST_FUZZ */ | |
6fc6879b JM |
989 | return eap_sim_client_error(data, id, |
990 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
23ddc7b8 | 991 | #endif /* TEST_FUZZ */ |
6fc6879b JM |
992 | } |
993 | ||
1c16b257 MS |
994 | /* At this stage the received MAC has been verified. Use this MAC for |
995 | * reauth Session-Id calculation if all other checks pass. | |
996 | * The peer does not use the local MAC but the received MAC in deriving | |
997 | * Session-Id. */ | |
23ddc7b8 JM |
998 | #ifdef TEST_FUZZ |
999 | if (attr->mac) | |
1000 | os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); | |
1001 | else | |
1002 | os_memset(data->reauth_mac, 0x12, EAP_SIM_MAC_LEN); | |
1003 | #else /* TEST_FUZZ */ | |
1c16b257 | 1004 | os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); |
23ddc7b8 | 1005 | #endif /* TEST_FUZZ */ |
1c16b257 MS |
1006 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: Server MAC", |
1007 | data->reauth_mac, EAP_SIM_MAC_LEN); | |
1008 | ||
6fc6879b JM |
1009 | if (attr->encr_data == NULL || attr->iv == NULL) { |
1010 | wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " | |
1011 | "message did not include encrypted data"); | |
1012 | return eap_sim_client_error(data, id, | |
1013 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1014 | } | |
1015 | ||
1016 | decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, | |
1017 | attr->encr_data_len, attr->iv, &eattr, | |
1018 | 0); | |
1019 | if (decrypted == NULL) { | |
1020 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " | |
1021 | "data from reauthentication message"); | |
1022 | return eap_sim_client_error(data, id, | |
1023 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1024 | } | |
1025 | ||
1026 | if (eattr.nonce_s == NULL || eattr.counter < 0) { | |
1027 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", | |
1028 | !eattr.nonce_s ? " AT_NONCE_S" : "", | |
1029 | eattr.counter < 0 ? " AT_COUNTER" : ""); | |
1030 | os_free(decrypted); | |
1031 | return eap_sim_client_error(data, id, | |
1032 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1033 | } | |
1034 | ||
1035 | if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { | |
04cad507 | 1036 | struct wpabuf *res; |
6fc6879b JM |
1037 | wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " |
1038 | "(%d <= %d)", eattr.counter, data->counter); | |
1039 | data->counter_too_small = eattr.counter; | |
04cad507 | 1040 | |
6fc6879b JM |
1041 | /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current |
1042 | * reauth_id must not be used to start a new reauthentication. | |
1043 | * However, since it was used in the last EAP-Response-Identity | |
1044 | * packet, it has to saved for the following fullauth to be | |
1045 | * used in MK derivation. */ | |
1046 | os_free(data->last_eap_identity); | |
1047 | data->last_eap_identity = data->reauth_id; | |
1048 | data->last_eap_identity_len = data->reauth_id_len; | |
1049 | data->reauth_id = NULL; | |
1050 | data->reauth_id_len = 0; | |
04cad507 JM |
1051 | |
1052 | res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s); | |
6fc6879b | 1053 | os_free(decrypted); |
04cad507 JM |
1054 | |
1055 | return res; | |
6fc6879b JM |
1056 | } |
1057 | data->counter = eattr.counter; | |
1058 | ||
1059 | os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); | |
1060 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", | |
1061 | data->nonce_s, EAP_SIM_NONCE_S_LEN); | |
1062 | ||
1063 | eap_sim_derive_keys_reauth(data->counter, | |
1064 | data->reauth_id, data->reauth_id_len, | |
1065 | data->nonce_s, data->mk, data->msk, | |
1066 | data->emsk); | |
e026159a | 1067 | eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); |
a6689be8 | 1068 | eap_sim_learn_ids(sm, data, &eattr); |
6fc6879b JM |
1069 | |
1070 | if (data->result_ind && attr->result_ind) | |
1071 | data->use_result_ind = 1; | |
1072 | ||
79122f9f | 1073 | if (data->state != FAILURE) { |
6fc6879b JM |
1074 | eap_sim_state(data, data->use_result_ind ? |
1075 | RESULT_SUCCESS : SUCCESS); | |
1076 | } | |
1077 | ||
1078 | data->num_id_req = 0; | |
1079 | data->num_notification = 0; | |
1080 | if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { | |
1081 | wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " | |
1082 | "fast reauths performed - force fullauth"); | |
e026159a JM |
1083 | eap_sim_clear_identities(sm, data, |
1084 | CLEAR_REAUTH_ID | CLEAR_EAP_ID); | |
6fc6879b JM |
1085 | } |
1086 | os_free(decrypted); | |
5d65ca51 | 1087 | return eap_sim_response_reauth(data, id, 0, data->nonce_s); |
6fc6879b JM |
1088 | } |
1089 | ||
1090 | ||
1091 | static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, | |
1092 | struct eap_method_ret *ret, | |
1093 | const struct wpabuf *reqData) | |
1094 | { | |
1095 | struct eap_sim_data *data = priv; | |
1096 | const struct eap_hdr *req; | |
1097 | u8 subtype, id; | |
1098 | struct wpabuf *res; | |
1099 | const u8 *pos; | |
1100 | struct eap_sim_attrs attr; | |
1101 | size_t len; | |
1102 | ||
1103 | wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData); | |
1104 | if (eap_get_config_identity(sm, &len) == NULL) { | |
1105 | wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); | |
1106 | eap_sm_request_identity(sm); | |
1107 | ret->ignore = TRUE; | |
1108 | return NULL; | |
1109 | } | |
1110 | ||
1111 | pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); | |
ff4a6d43 | 1112 | if (pos == NULL || len < 3) { |
6fc6879b JM |
1113 | ret->ignore = TRUE; |
1114 | return NULL; | |
1115 | } | |
1116 | req = wpabuf_head(reqData); | |
1117 | id = req->identifier; | |
1118 | len = be_to_host16(req->length); | |
1119 | ||
1120 | ret->ignore = FALSE; | |
1121 | ret->methodState = METHOD_MAY_CONT; | |
1122 | ret->decision = DECISION_FAIL; | |
1123 | ret->allowNotifications = TRUE; | |
1124 | ||
1125 | subtype = *pos++; | |
1126 | wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); | |
1127 | pos += 2; /* Reserved */ | |
1128 | ||
1129 | if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0, | |
1130 | 0)) { | |
1131 | res = eap_sim_client_error(data, id, | |
1132 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1133 | goto done; | |
1134 | } | |
1135 | ||
1136 | switch (subtype) { | |
1137 | case EAP_SIM_SUBTYPE_START: | |
1138 | res = eap_sim_process_start(sm, data, id, &attr); | |
1139 | break; | |
1140 | case EAP_SIM_SUBTYPE_CHALLENGE: | |
1141 | res = eap_sim_process_challenge(sm, data, id, reqData, &attr); | |
1142 | break; | |
1143 | case EAP_SIM_SUBTYPE_NOTIFICATION: | |
1144 | res = eap_sim_process_notification(sm, data, id, reqData, | |
1145 | &attr); | |
1146 | break; | |
1147 | case EAP_SIM_SUBTYPE_REAUTHENTICATION: | |
1148 | res = eap_sim_process_reauthentication(sm, data, id, reqData, | |
1149 | &attr); | |
1150 | break; | |
1151 | case EAP_SIM_SUBTYPE_CLIENT_ERROR: | |
1152 | wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); | |
1153 | res = eap_sim_client_error(data, id, | |
1154 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1155 | break; | |
1156 | default: | |
1157 | wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); | |
1158 | res = eap_sim_client_error(data, id, | |
1159 | EAP_SIM_UNABLE_TO_PROCESS_PACKET); | |
1160 | break; | |
1161 | } | |
1162 | ||
1163 | done: | |
1164 | if (data->state == FAILURE) { | |
1165 | ret->decision = DECISION_FAIL; | |
1166 | ret->methodState = METHOD_DONE; | |
1167 | } else if (data->state == SUCCESS) { | |
1168 | ret->decision = data->use_result_ind ? | |
1169 | DECISION_UNCOND_SUCC : DECISION_COND_SUCC; | |
1170 | ret->methodState = data->use_result_ind ? | |
1171 | METHOD_DONE : METHOD_MAY_CONT; | |
79122f9f | 1172 | } else if (data->state == RESULT_SUCCESS) |
6fc6879b JM |
1173 | ret->methodState = METHOD_CONT; |
1174 | ||
1175 | if (ret->methodState == METHOD_DONE) { | |
1176 | ret->allowNotifications = FALSE; | |
1177 | } | |
1178 | ||
1179 | return res; | |
1180 | } | |
1181 | ||
1182 | ||
1183 | static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) | |
1184 | { | |
1185 | struct eap_sim_data *data = priv; | |
1186 | return data->pseudonym || data->reauth_id; | |
1187 | } | |
1188 | ||
1189 | ||
1190 | static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) | |
1191 | { | |
1192 | struct eap_sim_data *data = priv; | |
e026159a | 1193 | eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); |
6fc6879b | 1194 | data->use_result_ind = 0; |
f534ee08 | 1195 | eap_sim_clear_keys(data, 1); |
6fc6879b JM |
1196 | } |
1197 | ||
1198 | ||
1199 | static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) | |
1200 | { | |
1201 | struct eap_sim_data *data = priv; | |
3642c431 | 1202 | if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { |
6fc6879b JM |
1203 | wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " |
1204 | "for NONCE_MT"); | |
ea52a46e | 1205 | eap_sim_deinit(sm, data); |
6fc6879b JM |
1206 | return NULL; |
1207 | } | |
1208 | data->num_id_req = 0; | |
1209 | data->num_notification = 0; | |
1210 | eap_sim_state(data, CONTINUE); | |
1211 | return priv; | |
1212 | } | |
1213 | ||
1214 | ||
1215 | static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, | |
1216 | size_t *len) | |
1217 | { | |
1218 | struct eap_sim_data *data = priv; | |
1219 | ||
1220 | if (data->reauth_id) { | |
1221 | *len = data->reauth_id_len; | |
1222 | return data->reauth_id; | |
1223 | } | |
1224 | ||
1225 | if (data->pseudonym) { | |
1226 | *len = data->pseudonym_len; | |
1227 | return data->pseudonym; | |
1228 | } | |
1229 | ||
1230 | return NULL; | |
1231 | } | |
1232 | ||
1233 | ||
1234 | static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) | |
1235 | { | |
1236 | struct eap_sim_data *data = priv; | |
1237 | return data->state == SUCCESS; | |
1238 | } | |
1239 | ||
1240 | ||
1241 | static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) | |
1242 | { | |
1243 | struct eap_sim_data *data = priv; | |
1244 | u8 *key; | |
1245 | ||
1246 | if (data->state != SUCCESS) | |
1247 | return NULL; | |
1248 | ||
a1f11e34 | 1249 | key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); |
6fc6879b JM |
1250 | if (key == NULL) |
1251 | return NULL; | |
1252 | ||
1253 | *len = EAP_SIM_KEYING_DATA_LEN; | |
6fc6879b JM |
1254 | |
1255 | return key; | |
1256 | } | |
1257 | ||
1258 | ||
9ca84274 JM |
1259 | static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) |
1260 | { | |
1261 | struct eap_sim_data *data = priv; | |
1262 | u8 *id; | |
1263 | ||
1264 | if (data->state != SUCCESS) | |
1265 | return NULL; | |
1266 | ||
1c16b257 MS |
1267 | if (!data->reauth) |
1268 | *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; | |
1269 | else | |
1270 | *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; | |
9ca84274 JM |
1271 | id = os_malloc(*len); |
1272 | if (id == NULL) | |
1273 | return NULL; | |
1274 | ||
1275 | id[0] = EAP_TYPE_SIM; | |
1c16b257 MS |
1276 | if (!data->reauth) { |
1277 | os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); | |
1278 | os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, | |
1279 | data->nonce_mt, EAP_SIM_NONCE_MT_LEN); | |
1280 | } else { | |
1281 | os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); | |
1282 | os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, | |
1283 | EAP_SIM_MAC_LEN); | |
1284 | } | |
9ca84274 JM |
1285 | wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); |
1286 | ||
1287 | return id; | |
1288 | } | |
1289 | ||
1290 | ||
6fc6879b JM |
1291 | static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) |
1292 | { | |
1293 | struct eap_sim_data *data = priv; | |
1294 | u8 *key; | |
1295 | ||
1296 | if (data->state != SUCCESS) | |
1297 | return NULL; | |
1298 | ||
a1f11e34 | 1299 | key = os_memdup(data->emsk, EAP_EMSK_LEN); |
6fc6879b JM |
1300 | if (key == NULL) |
1301 | return NULL; | |
1302 | ||
1303 | *len = EAP_EMSK_LEN; | |
6fc6879b JM |
1304 | |
1305 | return key; | |
1306 | } | |
1307 | ||
1308 | ||
45f7574d AE |
1309 | static int eap_sim_get_error_code(void *priv) |
1310 | { | |
1311 | struct eap_sim_data *data = priv; | |
1312 | int current_data_error; | |
1313 | ||
1314 | if (!data) | |
1315 | return NO_EAP_METHOD_ERROR; | |
1316 | ||
1317 | current_data_error = data->error_code; | |
1318 | ||
1319 | /* Now reset for next transaction */ | |
1320 | data->error_code = NO_EAP_METHOD_ERROR; | |
1321 | ||
1322 | return current_data_error; | |
1323 | } | |
1324 | ||
1325 | ||
6fc6879b JM |
1326 | int eap_peer_sim_register(void) |
1327 | { | |
1328 | struct eap_method *eap; | |
6fc6879b JM |
1329 | |
1330 | eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, | |
1331 | EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); | |
1332 | if (eap == NULL) | |
1333 | return -1; | |
1334 | ||
1335 | eap->init = eap_sim_init; | |
1336 | eap->deinit = eap_sim_deinit; | |
1337 | eap->process = eap_sim_process; | |
1338 | eap->isKeyAvailable = eap_sim_isKeyAvailable; | |
1339 | eap->getKey = eap_sim_getKey; | |
9ca84274 | 1340 | eap->getSessionId = eap_sim_get_session_id; |
6fc6879b JM |
1341 | eap->has_reauth_data = eap_sim_has_reauth_data; |
1342 | eap->deinit_for_reauth = eap_sim_deinit_for_reauth; | |
1343 | eap->init_for_reauth = eap_sim_init_for_reauth; | |
1344 | eap->get_identity = eap_sim_get_identity; | |
1345 | eap->get_emsk = eap_sim_get_emsk; | |
45f7574d | 1346 | eap->get_error_code = eap_sim_get_error_code; |
6fc6879b | 1347 | |
49a26bb3 | 1348 | return eap_peer_method_register(eap); |
6fc6879b | 1349 | } |