1118611Snjl/*
2118611Snjl * hostapd / EAP-SIM (RFC 4186)
3118611Snjl * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
4118611Snjl *
5118611Snjl * This program is free software; you can redistribute it and/or modify
6118611Snjl * it under the terms of the GNU General Public License version 2 as
7118611Snjl * published by the Free Software Foundation.
8217365Sjkim *
9229989Sjkim * Alternatively, this software may be distributed under the terms of BSD
10118611Snjl * license.
11118611Snjl *
12217365Sjkim * See README and COPYING for more details.
13217365Sjkim */
14217365Sjkim
15217365Sjkim#include "includes.h"
16217365Sjkim
17217365Sjkim#include "common.h"
18217365Sjkim#include "eap_server/eap_i.h"
19217365Sjkim#include "eap_common/eap_sim_common.h"
20217365Sjkim#include "eap_server/eap_sim_db.h"
21217365Sjkim
22217365Sjkim
23217365Sjkimstruct eap_sim_data {
24217365Sjkim	u8 mk[EAP_SIM_MK_LEN];
25217365Sjkim	u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
26118611Snjl	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
27217365Sjkim	u8 k_aut[EAP_SIM_K_AUT_LEN];
28217365Sjkim	u8 k_encr[EAP_SIM_K_ENCR_LEN];
29217365Sjkim	u8 msk[EAP_SIM_KEYING_DATA_LEN];
30118611Snjl	u8 emsk[EAP_EMSK_LEN];
31217365Sjkim	u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
32217365Sjkim	u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
33217365Sjkim	u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
34217365Sjkim	int num_chal;
35217365Sjkim	enum {
36217365Sjkim		START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
37217365Sjkim	} state;
38217365Sjkim	char *next_pseudonym;
39217365Sjkim	char *next_reauth_id;
40217365Sjkim	u16 counter;
41217365Sjkim	struct eap_sim_reauth *reauth;
42217365Sjkim	u16 notification;
43217365Sjkim	int use_result_ind;
44118611Snjl};
45118611Snjl
46118611Snjl
47118611Snjlstatic const char * eap_sim_state_txt(int state)
48151937Sjkim{
49193529Sjkim	switch (state) {
50210976Sjkim	case START:
51118611Snjl		return "START";
52118611Snjl	case CHALLENGE:
53118611Snjl		return "CHALLENGE";
54118611Snjl	case REAUTH:
55118611Snjl		return "REAUTH";
56118611Snjl	case SUCCESS:
57118611Snjl		return "SUCCESS";
58118611Snjl	case FAILURE:
59151937Sjkim		return "FAILURE";
60118611Snjl	case NOTIFICATION:
61151937Sjkim		return "NOTIFICATION";
62151937Sjkim	default:
63151937Sjkim		return "Unknown?!";
64151937Sjkim	}
65151937Sjkim}
66237412Sjkim
67151937Sjkim
68151937Sjkimstatic void eap_sim_state(struct eap_sim_data *data, int state)
69151937Sjkim{
70151937Sjkim	wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
71151937Sjkim		   eap_sim_state_txt(data->state),
72151937Sjkim		   eap_sim_state_txt(state));
73151937Sjkim	data->state = state;
74151937Sjkim}
75151937Sjkim
76151937Sjkim
77193529Sjkimstatic void * eap_sim_init(struct eap_sm *sm)
78151937Sjkim{
79151937Sjkim	struct eap_sim_data *data;
80151937Sjkim
81151937Sjkim	if (sm->eap_sim_db_priv == NULL) {
82197104Sjkim		wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
83197104Sjkim		return NULL;
84197104Sjkim	}
85197104Sjkim
86197104Sjkim	data = os_zalloc(sizeof(*data));
87151937Sjkim	if (data == NULL)
88197104Sjkim		return NULL;
89197104Sjkim	data->state = START;
90197104Sjkim
91197104Sjkim	return data;
92197104Sjkim}
93197104Sjkim
94197104Sjkim
95197104Sjkimstatic void eap_sim_reset(struct eap_sm *sm, void *priv)
96197104Sjkim{
97197104Sjkim	struct eap_sim_data *data = priv;
98197104Sjkim	os_free(data->next_pseudonym);
99238381Sjkim	os_free(data->next_reauth_id);
100197104Sjkim	os_free(data);
101197104Sjkim}
102118611Snjl
103118611Snjl
104118611Snjlstatic struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
105118611Snjl					   struct eap_sim_data *data, u8 id)
106118611Snjl{
107118611Snjl	struct eap_sim_msg *msg;
108118611Snjl	u8 ver[2];
109118611Snjl
110118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
111118611Snjl	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
112118611Snjl			       EAP_SIM_SUBTYPE_START);
113118611Snjl	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
114151937Sjkim				      sm->identity_len)) {
115118611Snjl		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
116118611Snjl		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
117118611Snjl	} else {
118118611Snjl		/*
119228110Sjkim		 * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
120228110Sjkim		 * ignored and the SIM/Start is used to request the identity.
121228110Sjkim		 */
122238381Sjkim		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
123197104Sjkim		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
124233250Sjkim	}
125233250Sjkim	wpa_printf(MSG_DEBUG, "   AT_VERSION_LIST");
126233250Sjkim	ver[0] = 0;
127233250Sjkim	ver[1] = EAP_SIM_VERSION;
128234623Sjkim	eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
129233250Sjkim			ver, sizeof(ver));
130197104Sjkim	return eap_sim_msg_finish(msg, NULL, NULL, 0);
131228110Sjkim}
132228110Sjkim
133228110Sjkim
134228110Sjkimstatic int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
135228110Sjkim			      struct eap_sim_msg *msg, u16 counter,
136228110Sjkim			      const u8 *nonce_s)
137233250Sjkim{
138234623Sjkim	os_free(data->next_pseudonym);
139118611Snjl	data->next_pseudonym =
140138287Smarks		eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
141233250Sjkim	os_free(data->next_reauth_id);
142233250Sjkim	if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
143233250Sjkim		data->next_reauth_id =
144118611Snjl			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
145138287Smarks	} else {
146228110Sjkim		wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
147228110Sjkim			   "count exceeded - force full authentication");
148228110Sjkim		data->next_reauth_id = NULL;
149228110Sjkim	}
150228110Sjkim
151228110Sjkim	if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
152118611Snjl	    counter == 0 && nonce_s == NULL)
153212761Sjkim		return 0;
154228110Sjkim
155228110Sjkim	wpa_printf(MSG_DEBUG, "   AT_IV");
156228110Sjkim	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
157118611Snjl	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
158209746Sjkim
159228110Sjkim	if (counter > 0) {
160228110Sjkim		wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
161228110Sjkim		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
162209746Sjkim	}
163118611Snjl
164228110Sjkim	if (nonce_s) {
165228110Sjkim		wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
166228110Sjkim		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
167228110Sjkim				EAP_SIM_NONCE_S_LEN);
168228110Sjkim	}
169228110Sjkim
170228110Sjkim	if (data->next_pseudonym) {
171228110Sjkim		wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
172118611Snjl			   data->next_pseudonym);
173118611Snjl		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
174237412Sjkim				os_strlen(data->next_pseudonym),
175228110Sjkim				(u8 *) data->next_pseudonym,
176237412Sjkim				os_strlen(data->next_pseudonym));
177228110Sjkim	}
178228110Sjkim
179237412Sjkim	if (data->next_reauth_id) {
180237412Sjkim		wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
181237412Sjkim			   data->next_reauth_id);
182237412Sjkim		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
183237412Sjkim				os_strlen(data->next_reauth_id),
184237412Sjkim				(u8 *) data->next_reauth_id,
185237412Sjkim				os_strlen(data->next_reauth_id));
186237412Sjkim	}
187118611Snjl
188118611Snjl	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
189118611Snjl		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
190118611Snjl			   "AT_ENCR_DATA");
191118611Snjl		return -1;
192237412Sjkim	}
193118611Snjl
194118611Snjl	return 0;
195118611Snjl}
196118611Snjl
197118611Snjl
198237412Sjkimstatic struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
199118611Snjl					       struct eap_sim_data *data,
200118611Snjl					       u8 id)
201118611Snjl{
202151937Sjkim	struct eap_sim_msg *msg;
203237412Sjkim
204118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
205118611Snjl	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
206118611Snjl			       EAP_SIM_SUBTYPE_CHALLENGE);
207228110Sjkim	wpa_printf(MSG_DEBUG, "   AT_RAND");
208118611Snjl	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
209118611Snjl			data->num_chal * GSM_RAND_LEN);
210118611Snjl
211118611Snjl	if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
212118611Snjl		eap_sim_msg_free(msg);
213118611Snjl		return NULL;
214118611Snjl	}
215118611Snjl
216118611Snjl	if (sm->eap_sim_aka_result_ind) {
217118611Snjl		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
218118611Snjl		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
219118611Snjl	}
220118611Snjl
221118611Snjl	wpa_printf(MSG_DEBUG, "   AT_MAC");
222118611Snjl	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
223118611Snjl	return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
224118611Snjl				  EAP_SIM_NONCE_MT_LEN);
225118611Snjl}
226118611Snjl
227118611Snjl
228118611Snjlstatic struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
229118611Snjl					    struct eap_sim_data *data, u8 id)
230151937Sjkim{
231118611Snjl	struct eap_sim_msg *msg;
232118611Snjl
233118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
234118611Snjl
235228110Sjkim	if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
236228110Sjkim		return NULL;
237118611Snjl	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
238118611Snjl			data->nonce_s, EAP_SIM_NONCE_S_LEN);
239118611Snjl
240118611Snjl	eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
241118611Snjl			    data->emsk);
242118611Snjl	eap_sim_derive_keys_reauth(data->counter, sm->identity,
243118611Snjl				   sm->identity_len, data->nonce_s, data->mk,
244118611Snjl				   data->msk, data->emsk);
245118611Snjl
246118611Snjl	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
247118611Snjl			       EAP_SIM_SUBTYPE_REAUTHENTICATION);
248118611Snjl
249118611Snjl	if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
250118611Snjl		eap_sim_msg_free(msg);
251118611Snjl		return NULL;
252118611Snjl	}
253151937Sjkim
254151937Sjkim	if (sm->eap_sim_aka_result_ind) {
255151937Sjkim		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
256118611Snjl		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
257118611Snjl	}
258118611Snjl
259118611Snjl	wpa_printf(MSG_DEBUG, "   AT_MAC");
260118611Snjl	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
261118611Snjl	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
262118611Snjl}
263118611Snjl
264118611Snjl
265118611Snjlstatic struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
266118611Snjl						  struct eap_sim_data *data,
267118611Snjl						  u8 id)
268118611Snjl{
269118611Snjl	struct eap_sim_msg *msg;
270118611Snjl
271118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
272118611Snjl	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
273118611Snjl			       EAP_SIM_SUBTYPE_NOTIFICATION);
274118611Snjl	wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
275118611Snjl	eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
276118611Snjl			NULL, 0);
277118611Snjl	if (data->use_result_ind) {
278118611Snjl		if (data->reauth) {
279118611Snjl			wpa_printf(MSG_DEBUG, "   AT_IV");
280118611Snjl			wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
281118611Snjl			eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
282197104Sjkim						   EAP_SIM_AT_ENCR_DATA);
283118611Snjl			wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
284197104Sjkim				   data->counter);
285197104Sjkim			eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
286118611Snjl					NULL, 0);
287118611Snjl
288118611Snjl			if (eap_sim_msg_add_encr_end(msg, data->k_encr,
289197104Sjkim						     EAP_SIM_AT_PADDING)) {
290118611Snjl				wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
291118611Snjl					   "encrypt AT_ENCR_DATA");
292118611Snjl				eap_sim_msg_free(msg);
293197104Sjkim				return NULL;
294197104Sjkim			}
295197104Sjkim		}
296197104Sjkim
297197104Sjkim		wpa_printf(MSG_DEBUG, "   AT_MAC");
298197104Sjkim		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
299197104Sjkim	}
300197104Sjkim	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
301197104Sjkim}
302197104Sjkim
303197104Sjkim
304197104Sjkimstatic struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
305197104Sjkim{
306197104Sjkim	struct eap_sim_data *data = priv;
307197104Sjkim
308197104Sjkim	switch (data->state) {
309197104Sjkim	case START:
310197104Sjkim		return eap_sim_build_start(sm, data, id);
311197104Sjkim	case CHALLENGE:
312197104Sjkim		return eap_sim_build_challenge(sm, data, id);
313197104Sjkim	case REAUTH:
314197104Sjkim		return eap_sim_build_reauth(sm, data, id);
315197104Sjkim	case NOTIFICATION:
316197104Sjkim		return eap_sim_build_notification(sm, data, id);
317197104Sjkim	default:
318197104Sjkim		wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
319197104Sjkim			   "buildReq", data->state);
320197104Sjkim		break;
321197104Sjkim	}
322197104Sjkim	return NULL;
323197104Sjkim}
324193529Sjkim
325197104Sjkim
326197104Sjkimstatic Boolean eap_sim_check(struct eap_sm *sm, void *priv,
327118611Snjl			     struct wpabuf *respData)
328197104Sjkim{
329197104Sjkim	struct eap_sim_data *data = priv;
330197104Sjkim	const u8 *pos;
331197104Sjkim	size_t len;
332197104Sjkim	u8 subtype;
333118611Snjl
334118611Snjl	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
335197104Sjkim	if (pos == NULL || len < 3) {
336197104Sjkim		wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
337197104Sjkim		return TRUE;
338197104Sjkim	}
339197104Sjkim	subtype = *pos;
340197104Sjkim
341197104Sjkim	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
342118611Snjl		return FALSE;
343197104Sjkim
344197104Sjkim	switch (data->state) {
345197104Sjkim	case START:
346197104Sjkim		if (subtype != EAP_SIM_SUBTYPE_START) {
347197104Sjkim			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
348197104Sjkim				   "subtype %d", subtype);
349197104Sjkim			return TRUE;
350197104Sjkim		}
351197104Sjkim		break;
352197104Sjkim	case CHALLENGE:
353118611Snjl		if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
354197104Sjkim			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
355197104Sjkim				   "subtype %d", subtype);
356197104Sjkim			return TRUE;
357197104Sjkim		}
358197104Sjkim		break;
359197104Sjkim	case REAUTH:
360197104Sjkim		if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
361197104Sjkim			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
362197104Sjkim				   "subtype %d", subtype);
363197104Sjkim			return TRUE;
364197104Sjkim		}
365197104Sjkim		break;
366197104Sjkim	case NOTIFICATION:
367197104Sjkim		if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
368197104Sjkim			wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
369197104Sjkim				   "subtype %d", subtype);
370118611Snjl			return TRUE;
371118611Snjl		}
372197104Sjkim		break;
373197104Sjkim	default:
374197104Sjkim		wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
375197104Sjkim			   "processing a response", data->state);
376197104Sjkim		return TRUE;
377197104Sjkim	}
378197104Sjkim
379197104Sjkim	return FALSE;
380197104Sjkim}
381197104Sjkim
382197104Sjkim
383197104Sjkimstatic int eap_sim_supported_ver(struct eap_sim_data *data, int version)
384197104Sjkim{
385197104Sjkim	return version == EAP_SIM_VERSION;
386197104Sjkim}
387197104Sjkim
388197104Sjkim
389197104Sjkimstatic void eap_sim_process_start(struct eap_sm *sm,
390197104Sjkim				  struct eap_sim_data *data,
391197104Sjkim				  struct wpabuf *respData,
392197104Sjkim				  struct eap_sim_attrs *attr)
393197104Sjkim{
394197104Sjkim	const u8 *identity;
395197104Sjkim	size_t identity_len;
396197104Sjkim	u8 ver_list[2];
397197104Sjkim
398197104Sjkim	wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
399197104Sjkim
400197104Sjkim	if (attr->identity) {
401197104Sjkim		os_free(sm->identity);
402212761Sjkim		sm->identity = os_malloc(attr->identity_len);
403197104Sjkim		if (sm->identity) {
404197104Sjkim			os_memcpy(sm->identity, attr->identity,
405118611Snjl				  attr->identity_len);
406118611Snjl			sm->identity_len = attr->identity_len;
407197104Sjkim		}
408118611Snjl	}
409197104Sjkim
410197104Sjkim	identity = NULL;
411197104Sjkim	identity_len = 0;
412197104Sjkim
413197104Sjkim	if (sm->identity && sm->identity_len > 0 &&
414233250Sjkim	    sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
415197104Sjkim		identity = sm->identity;
416197104Sjkim		identity_len = sm->identity_len;
417197104Sjkim	} else {
418197104Sjkim		identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
419233250Sjkim						    sm->identity,
420197104Sjkim						    sm->identity_len,
421197104Sjkim						    &identity_len);
422197104Sjkim		if (identity == NULL) {
423197104Sjkim			data->reauth = eap_sim_db_get_reauth_entry(
424233250Sjkim				sm->eap_sim_db_priv, sm->identity,
425151937Sjkim				sm->identity_len);
426151937Sjkim			if (data->reauth) {
427151937Sjkim				wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
428151937Sjkim					   "re-authentication");
429233250Sjkim				identity = data->reauth->identity;
430118611Snjl				identity_len = data->reauth->identity_len;
431118611Snjl				data->counter = data->reauth->counter;
432237412Sjkim				os_memcpy(data->mk, data->reauth->mk,
433118611Snjl					  EAP_SIM_MK_LEN);
434220663Sjkim			}
435233250Sjkim		}
436118611Snjl	}
437118611Snjl
438118611Snjl	if (identity == NULL) {
439118611Snjl		wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
440118611Snjl			   " user name");
441118611Snjl		eap_sim_state(data, FAILURE);
442118611Snjl		return;
443197104Sjkim	}
444118611Snjl
445118611Snjl	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
446118611Snjl			  identity, identity_len);
447118611Snjl
448118611Snjl	if (data->reauth) {
449118611Snjl		eap_sim_state(data, REAUTH);
450118611Snjl		return;
451118611Snjl	}
452118611Snjl
453207344Sjkim	if (attr->nonce_mt == NULL || attr->selected_version < 0) {
454207344Sjkim		wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
455207344Sjkim			   "required attributes");
456207344Sjkim		eap_sim_state(data, FAILURE);
457207344Sjkim		return;
458118611Snjl	}
459207344Sjkim
460207344Sjkim	if (!eap_sim_supported_ver(data, attr->selected_version)) {
461207344Sjkim		wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
462207344Sjkim			   "version %d", attr->selected_version);
463118611Snjl		eap_sim_state(data, FAILURE);
464118611Snjl		return;
465118611Snjl	}
466233250Sjkim
467118611Snjl	data->counter = 0; /* reset re-auth counter since this is full auth */
468118611Snjl	data->reauth = NULL;
469118611Snjl
470193529Sjkim	data->num_chal = eap_sim_db_get_gsm_triplets(
471118611Snjl		sm->eap_sim_db_priv, identity, identity_len,
472118611Snjl		EAP_SIM_MAX_CHAL,
473210976Sjkim		(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
474210976Sjkim	if (data->num_chal == EAP_SIM_DB_PENDING) {
475210976Sjkim		wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
476210976Sjkim			   "not yet available - pending request");
477210976Sjkim		sm->method_pending = METHOD_PENDING_WAIT;
478118611Snjl		return;
479118611Snjl	}
480118611Snjl	if (data->num_chal < 2) {
481118611Snjl		wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
482118611Snjl			   "authentication triplets for the peer");
483197104Sjkim		eap_sim_state(data, FAILURE);
484118611Snjl		return;
485118611Snjl	}
486118611Snjl
487118611Snjl	identity_len = sm->identity_len;
488118611Snjl	while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
489118611Snjl		wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
490233250Sjkim			   "character from identity");
491233250Sjkim		identity_len--;
492233250Sjkim	}
493233250Sjkim	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
494233250Sjkim			  sm->identity, identity_len);
495233250Sjkim
496212761Sjkim	os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
497212761Sjkim	WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
498212761Sjkim	eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
499212761Sjkim			  attr->selected_version, ver_list, sizeof(ver_list),
500212761Sjkim			  data->num_chal, (const u8 *) data->kc, data->mk);
501212761Sjkim	eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
502118611Snjl			    data->emsk);
503118611Snjl
504118611Snjl	eap_sim_state(data, CHALLENGE);
505233250Sjkim}
506233250Sjkim
507233250Sjkim
508118611Snjlstatic void eap_sim_process_challenge(struct eap_sm *sm,
509118611Snjl				      struct eap_sim_data *data,
510233250Sjkim				      struct wpabuf *respData,
511233250Sjkim				      struct eap_sim_attrs *attr)
512118611Snjl{
513118611Snjl	const u8 *identity;
514118611Snjl	size_t identity_len;
515233250Sjkim
516118611Snjl	if (attr->mac == NULL ||
517118611Snjl	    eap_sim_verify_mac(data->k_aut, respData, attr->mac,
518193529Sjkim			       (u8 *) data->sres,
519118611Snjl			       data->num_chal * EAP_SIM_SRES_LEN)) {
520118611Snjl		wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
521118611Snjl			   "did not include valid AT_MAC");
522118611Snjl		eap_sim_state(data, FAILURE);
523118611Snjl		return;
524118611Snjl	}
525118611Snjl
526237412Sjkim	wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
527118611Snjl		   "correct AT_MAC");
528118611Snjl	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
529118611Snjl		data->use_result_ind = 1;
530118611Snjl		data->notification = EAP_SIM_SUCCESS;
531118611Snjl		eap_sim_state(data, NOTIFICATION);
532118611Snjl	} else
533237412Sjkim		eap_sim_state(data, SUCCESS);
534237412Sjkim
535237412Sjkim	identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
536237412Sjkim					    sm->identity_len, &identity_len);
537118611Snjl	if (identity == NULL) {
538118611Snjl		identity = sm->identity;
539118611Snjl		identity_len = sm->identity_len;
540204773Sjkim	}
541118611Snjl
542118611Snjl	if (data->next_pseudonym) {
543209746Sjkim		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
544209746Sjkim					 identity_len,
545209746Sjkim					 data->next_pseudonym);
546209746Sjkim		data->next_pseudonym = NULL;
547118611Snjl	}
548118611Snjl	if (data->next_reauth_id) {
549197104Sjkim		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
550118611Snjl				      identity_len,
551118611Snjl				      data->next_reauth_id, data->counter + 1,
552118611Snjl				      data->mk);
553233250Sjkim		data->next_reauth_id = NULL;
554197104Sjkim	}
555197104Sjkim}
556197104Sjkim
557197104Sjkim
558233250Sjkimstatic void eap_sim_process_reauth(struct eap_sm *sm,
559118611Snjl				   struct eap_sim_data *data,
560118611Snjl				   struct wpabuf *respData,
561118611Snjl				   struct eap_sim_attrs *attr)
562118611Snjl{
563118611Snjl	struct eap_sim_attrs eattr;
564118611Snjl	u8 *decrypted = NULL;
565118611Snjl	const u8 *identity, *id2;
566118611Snjl	size_t identity_len, id2_len;
567118611Snjl
568118611Snjl	if (attr->mac == NULL ||
569118611Snjl	    eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
570118611Snjl			       EAP_SIM_NONCE_S_LEN)) {
571118611Snjl		wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
572118611Snjl			   "did not include valid AT_MAC");
573118611Snjl		goto fail;
574118611Snjl	}
575118611Snjl
576233250Sjkim	if (attr->encr_data == NULL || attr->iv == NULL) {
577197104Sjkim		wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
578118611Snjl			   "message did not include encrypted data");
579118611Snjl		goto fail;
580118611Snjl	}
581118611Snjl
582233250Sjkim	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
583118611Snjl				       attr->encr_data_len, attr->iv, &eattr,
584118611Snjl				       0);
585118611Snjl	if (decrypted == NULL) {
586118611Snjl		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
587118611Snjl			   "data from reauthentication message");
588118611Snjl		goto fail;
589118611Snjl	}
590118611Snjl
591233250Sjkim	if (eattr.counter != data->counter) {
592233250Sjkim		wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
593233250Sjkim			   "used incorrect counter %u, expected %u",
594233250Sjkim			   eattr.counter, data->counter);
595233250Sjkim		goto fail;
596233250Sjkim	}
597118611Snjl	os_free(decrypted);
598118611Snjl	decrypted = NULL;
599118611Snjl
600118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
601118611Snjl		   "the correct AT_MAC");
602118611Snjl	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
603118611Snjl		data->use_result_ind = 1;
604118611Snjl		data->notification = EAP_SIM_SUCCESS;
605118611Snjl		eap_sim_state(data, NOTIFICATION);
606118611Snjl	} else
607118611Snjl		eap_sim_state(data, SUCCESS);
608118611Snjl
609118611Snjl	if (data->reauth) {
610118611Snjl		identity = data->reauth->identity;
611197104Sjkim		identity_len = data->reauth->identity_len;
612118611Snjl	} else {
613118611Snjl		identity = sm->identity;
614118611Snjl		identity_len = sm->identity_len;
615118611Snjl	}
616233250Sjkim
617228110Sjkim	id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
618228110Sjkim				       identity_len, &id2_len);
619228110Sjkim	if (id2) {
620228110Sjkim		identity = id2;
621233250Sjkim		identity_len = id2_len;
622228110Sjkim	}
623228110Sjkim
624228110Sjkim	if (data->next_pseudonym) {
625228110Sjkim		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
626233250Sjkim					 identity_len, data->next_pseudonym);
627118611Snjl		data->next_pseudonym = NULL;
628118611Snjl	}
629118611Snjl	if (data->next_reauth_id) {
630118611Snjl		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
631118611Snjl				      identity_len, data->next_reauth_id,
632118611Snjl				      data->counter + 1, data->mk);
633118611Snjl		data->next_reauth_id = NULL;
634118611Snjl	} else {
635118611Snjl		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
636118611Snjl		data->reauth = NULL;
637118611Snjl	}
638118611Snjl
639118611Snjl	return;
640118611Snjl
641118611Snjlfail:
642118611Snjl	eap_sim_state(data, FAILURE);
643118611Snjl	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
644118611Snjl	data->reauth = NULL;
645118611Snjl	os_free(decrypted);
646118611Snjl}
647118611Snjl
648118611Snjl
649118611Snjlstatic void eap_sim_process_client_error(struct eap_sm *sm,
650118611Snjl					 struct eap_sim_data *data,
651118611Snjl					 struct wpabuf *respData,
652118611Snjl					 struct eap_sim_attrs *attr)
653118611Snjl{
654118611Snjl	wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
655118611Snjl		   attr->client_error_code);
656118611Snjl	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
657118611Snjl		eap_sim_state(data, SUCCESS);
658118611Snjl	else
659118611Snjl		eap_sim_state(data, FAILURE);
660118611Snjl}
661118611Snjl
662118611Snjl
663118611Snjlstatic void eap_sim_process_notification(struct eap_sm *sm,
664118611Snjl					 struct eap_sim_data *data,
665118611Snjl					 struct wpabuf *respData,
666118611Snjl					 struct eap_sim_attrs *attr)
667118611Snjl{
668197104Sjkim	wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
669118611Snjl	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
670118611Snjl		eap_sim_state(data, SUCCESS);
671118611Snjl	else
672118611Snjl		eap_sim_state(data, FAILURE);
673234623Sjkim}
674234623Sjkim
675234623Sjkim
676234623Sjkimstatic void eap_sim_process(struct eap_sm *sm, void *priv,
677234623Sjkim			    struct wpabuf *respData)
678234623Sjkim{
679234623Sjkim	struct eap_sim_data *data = priv;
680234623Sjkim	const u8 *pos, *end;
681234623Sjkim	u8 subtype;
682234623Sjkim	size_t len;
683234623Sjkim	struct eap_sim_attrs attr;
684234623Sjkim
685234623Sjkim	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
686234623Sjkim	if (pos == NULL || len < 3)
687234623Sjkim		return;
688234623Sjkim
689233250Sjkim	end = pos + len;
690118611Snjl	subtype = *pos;
691118611Snjl	pos += 3;
692233250Sjkim
693118611Snjl	if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
694118611Snjl		wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
695118611Snjl		eap_sim_state(data, FAILURE);
696118611Snjl		return;
697118611Snjl	}
698233250Sjkim
699138287Smarks	if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
700118611Snjl		eap_sim_process_client_error(sm, data, respData, &attr);
701118611Snjl		return;
702118611Snjl	}
703233250Sjkim
704118611Snjl	switch (data->state) {
705118611Snjl	case START:
706118611Snjl		eap_sim_process_start(sm, data, respData, &attr);
707118611Snjl		break;
708118611Snjl	case CHALLENGE:
709118611Snjl		eap_sim_process_challenge(sm, data, respData, &attr);
710118611Snjl		break;
711118611Snjl	case REAUTH:
712118611Snjl		eap_sim_process_reauth(sm, data, respData, &attr);
713118611Snjl		break;
714118611Snjl	case NOTIFICATION:
715118611Snjl		eap_sim_process_notification(sm, data, respData, &attr);
716118611Snjl		break;
717118611Snjl	default:
718118611Snjl		wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
719118611Snjl			   "process", data->state);
720118611Snjl		break;
721118611Snjl	}
722197104Sjkim}
723118611Snjl
724118611Snjl
725118611Snjlstatic Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
726118611Snjl{
727233250Sjkim	struct eap_sim_data *data = priv;
728118611Snjl	return data->state == SUCCESS || data->state == FAILURE;
729118611Snjl}
730118611Snjl
731118611Snjl
732118611Snjlstatic u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
733118611Snjl{
734118611Snjl	struct eap_sim_data *data = priv;
735118611Snjl	u8 *key;
736118611Snjl
737118611Snjl	if (data->state != SUCCESS)
738207344Sjkim		return NULL;
739207344Sjkim
740207344Sjkim	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
741207344Sjkim	if (key == NULL)
742118611Snjl		return NULL;
743118611Snjl	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
744197104Sjkim	*len = EAP_SIM_KEYING_DATA_LEN;
745118611Snjl	return key;
746118611Snjl}
747118611Snjl
748118611Snjl
749233250Sjkimstatic u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
750209746Sjkim{
751209746Sjkim	struct eap_sim_data *data = priv;
752209746Sjkim	u8 *key;
753209746Sjkim
754209746Sjkim	if (data->state != SUCCESS)
755238381Sjkim		return NULL;
756118611Snjl
757118611Snjl	key = os_malloc(EAP_EMSK_LEN);
758238381Sjkim	if (key == NULL)
759238381Sjkim		return NULL;
760238381Sjkim	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
761238381Sjkim	*len = EAP_EMSK_LEN;
762193529Sjkim	return key;
763193529Sjkim}
764193529Sjkim
765193529Sjkim
766193529Sjkimstatic Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
767193529Sjkim{
768118611Snjl	struct eap_sim_data *data = priv;
769235945Sjkim	return data->state == SUCCESS;
770235945Sjkim}
771235945Sjkim
772235945Sjkim
773235945Sjkimint eap_server_sim_register(void)
774235945Sjkim{
775235945Sjkim	struct eap_method *eap;
776235945Sjkim	int ret;
777235945Sjkim
778118611Snjl	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
779235945Sjkim				      EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
780235945Sjkim	if (eap == NULL)
781118611Snjl		return -1;
782118611Snjl
783118611Snjl	eap->init = eap_sim_init;
784118611Snjl	eap->reset = eap_sim_reset;
785118611Snjl	eap->buildReq = eap_sim_buildReq;
786118611Snjl	eap->check = eap_sim_check;
787118611Snjl	eap->process = eap_sim_process;
788118611Snjl	eap->isDone = eap_sim_isDone;
789118611Snjl	eap->getKey = eap_sim_getKey;
790118611Snjl	eap->isSuccess = eap_sim_isSuccess;
791118611Snjl	eap->get_emsk = eap_sim_get_emsk;
792193529Sjkim
793118611Snjl	ret = eap_server_method_register(eap);
794118611Snjl	if (ret)
795209746Sjkim		eap_server_method_free(eap);
796209746Sjkim	return ret;
797209746Sjkim}
798209746Sjkim