1e28a4053SRui Paulo /*
2e28a4053SRui Paulo * hostapd / EAP-SIM (RFC 4186)
3f05cddf9SRui Paulo * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7e28a4053SRui Paulo */
8e28a4053SRui Paulo
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo
11e28a4053SRui Paulo #include "common.h"
12*a90b9d01SCy Schubert #include "utils/base64.h"
13*a90b9d01SCy Schubert #include "crypto/crypto.h"
14f05cddf9SRui Paulo #include "crypto/random.h"
15e28a4053SRui Paulo #include "eap_server/eap_i.h"
16e28a4053SRui Paulo #include "eap_common/eap_sim_common.h"
17e28a4053SRui Paulo #include "eap_server/eap_sim_db.h"
18e28a4053SRui Paulo
19e28a4053SRui Paulo
20e28a4053SRui Paulo struct eap_sim_data {
21e28a4053SRui Paulo u8 mk[EAP_SIM_MK_LEN];
22e28a4053SRui Paulo u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
23e28a4053SRui Paulo u8 nonce_s[EAP_SIM_NONCE_S_LEN];
24e28a4053SRui Paulo u8 k_aut[EAP_SIM_K_AUT_LEN];
25e28a4053SRui Paulo u8 k_encr[EAP_SIM_K_ENCR_LEN];
26e28a4053SRui Paulo u8 msk[EAP_SIM_KEYING_DATA_LEN];
27e28a4053SRui Paulo u8 emsk[EAP_EMSK_LEN];
28e28a4053SRui Paulo u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
29e28a4053SRui Paulo u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
30e28a4053SRui Paulo u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
31206b73d0SCy Schubert u8 reauth_mac[EAP_SIM_MAC_LEN];
32e28a4053SRui Paulo int num_chal;
33e28a4053SRui Paulo enum {
34e28a4053SRui Paulo START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
35e28a4053SRui Paulo } state;
36e28a4053SRui Paulo char *next_pseudonym;
37e28a4053SRui Paulo char *next_reauth_id;
38e28a4053SRui Paulo u16 counter;
39e28a4053SRui Paulo struct eap_sim_reauth *reauth;
40e28a4053SRui Paulo u16 notification;
41e28a4053SRui Paulo int use_result_ind;
42f05cddf9SRui Paulo int start_round;
43f05cddf9SRui Paulo char permanent[20]; /* Permanent username */
44e28a4053SRui Paulo };
45e28a4053SRui Paulo
46e28a4053SRui Paulo
eap_sim_state_txt(int state)47e28a4053SRui Paulo static const char * eap_sim_state_txt(int state)
48e28a4053SRui Paulo {
49e28a4053SRui Paulo switch (state) {
50e28a4053SRui Paulo case START:
51e28a4053SRui Paulo return "START";
52e28a4053SRui Paulo case CHALLENGE:
53e28a4053SRui Paulo return "CHALLENGE";
54e28a4053SRui Paulo case REAUTH:
55e28a4053SRui Paulo return "REAUTH";
56e28a4053SRui Paulo case SUCCESS:
57e28a4053SRui Paulo return "SUCCESS";
58e28a4053SRui Paulo case FAILURE:
59e28a4053SRui Paulo return "FAILURE";
60e28a4053SRui Paulo case NOTIFICATION:
61e28a4053SRui Paulo return "NOTIFICATION";
62e28a4053SRui Paulo default:
63e28a4053SRui Paulo return "Unknown?!";
64e28a4053SRui Paulo }
65e28a4053SRui Paulo }
66e28a4053SRui Paulo
67e28a4053SRui Paulo
eap_sim_state(struct eap_sim_data * data,int state)68e28a4053SRui Paulo static void eap_sim_state(struct eap_sim_data *data, int state)
69e28a4053SRui Paulo {
70e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
71e28a4053SRui Paulo eap_sim_state_txt(data->state),
72e28a4053SRui Paulo eap_sim_state_txt(state));
73e28a4053SRui Paulo data->state = state;
74e28a4053SRui Paulo }
75e28a4053SRui Paulo
76e28a4053SRui Paulo
eap_sim_init(struct eap_sm * sm)77e28a4053SRui Paulo static void * eap_sim_init(struct eap_sm *sm)
78e28a4053SRui Paulo {
79e28a4053SRui Paulo struct eap_sim_data *data;
80e28a4053SRui Paulo
81c1d255d3SCy Schubert if (!sm->cfg->eap_sim_db_priv) {
82e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
83e28a4053SRui Paulo return NULL;
84e28a4053SRui Paulo }
85e28a4053SRui Paulo
86e28a4053SRui Paulo data = os_zalloc(sizeof(*data));
87e28a4053SRui Paulo if (data == NULL)
88e28a4053SRui Paulo return NULL;
89e28a4053SRui Paulo data->state = START;
90e28a4053SRui Paulo
91e28a4053SRui Paulo return data;
92e28a4053SRui Paulo }
93e28a4053SRui Paulo
94e28a4053SRui Paulo
eap_sim_reset(struct eap_sm * sm,void * priv)95e28a4053SRui Paulo static void eap_sim_reset(struct eap_sm *sm, void *priv)
96e28a4053SRui Paulo {
97e28a4053SRui Paulo struct eap_sim_data *data = priv;
98e28a4053SRui Paulo os_free(data->next_pseudonym);
99e28a4053SRui Paulo os_free(data->next_reauth_id);
1005b9c547cSRui Paulo bin_clear_free(data, sizeof(*data));
101e28a4053SRui Paulo }
102e28a4053SRui Paulo
103e28a4053SRui Paulo
eap_sim_build_start(struct eap_sm * sm,struct eap_sim_data * data,u8 id)104e28a4053SRui Paulo static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
105e28a4053SRui Paulo struct eap_sim_data *data, u8 id)
106e28a4053SRui Paulo {
107e28a4053SRui Paulo struct eap_sim_msg *msg;
108e28a4053SRui Paulo u8 ver[2];
109*a90b9d01SCy Schubert bool id_req = true;
110e28a4053SRui Paulo
111e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
112e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
113e28a4053SRui Paulo EAP_SIM_SUBTYPE_START);
114f05cddf9SRui Paulo data->start_round++;
115*a90b9d01SCy Schubert
116*a90b9d01SCy Schubert if (data->start_round == 1 && (sm->cfg->eap_sim_id & 0x04)) {
117*a90b9d01SCy Schubert char *username;
118*a90b9d01SCy Schubert
119*a90b9d01SCy Schubert username = sim_get_username(sm->identity, sm->identity_len);
120*a90b9d01SCy Schubert if (username && username[0] == EAP_SIM_REAUTH_ID_PREFIX &&
121*a90b9d01SCy Schubert eap_sim_db_get_reauth_entry(sm->cfg->eap_sim_db_priv,
122*a90b9d01SCy Schubert username))
123*a90b9d01SCy Schubert id_req = false;
124*a90b9d01SCy Schubert
125*a90b9d01SCy Schubert os_free(username);
126*a90b9d01SCy Schubert }
127*a90b9d01SCy Schubert
128*a90b9d01SCy Schubert if (!id_req) {
129*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, " No identity request");
130*a90b9d01SCy Schubert } else if (data->start_round == 1) {
131e28a4053SRui Paulo /*
132e28a4053SRui Paulo * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
133e28a4053SRui Paulo * ignored and the SIM/Start is used to request the identity.
134e28a4053SRui Paulo */
135e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
136e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
137f05cddf9SRui Paulo } else if (data->start_round > 3) {
138f05cddf9SRui Paulo /* Cannot use more than three rounds of Start messages */
139f05cddf9SRui Paulo eap_sim_msg_free(msg);
140f05cddf9SRui Paulo return NULL;
141f05cddf9SRui Paulo } else if (data->start_round == 0) {
142f05cddf9SRui Paulo /*
143f05cddf9SRui Paulo * This is a special case that is used to recover from
144f05cddf9SRui Paulo * AT_COUNTER_TOO_SMALL during re-authentication. Since we
145f05cddf9SRui Paulo * already know the identity of the peer, there is no need to
146f05cddf9SRui Paulo * request any identity in this case.
147f05cddf9SRui Paulo */
148f05cddf9SRui Paulo } else if (sm->identity && sm->identity_len > 0 &&
149f05cddf9SRui Paulo sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
150f05cddf9SRui Paulo /* Reauth id may have expired - try fullauth */
151f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ");
152f05cddf9SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
153f05cddf9SRui Paulo } else {
154f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
155f05cddf9SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
156e28a4053SRui Paulo }
157e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
158e28a4053SRui Paulo ver[0] = 0;
159e28a4053SRui Paulo ver[1] = EAP_SIM_VERSION;
160e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
161e28a4053SRui Paulo ver, sizeof(ver));
1625b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
163e28a4053SRui Paulo }
164e28a4053SRui Paulo
165e28a4053SRui Paulo
eap_sim_build_encr(struct eap_sm * sm,struct eap_sim_data * data,struct eap_sim_msg * msg,u16 counter,const u8 * nonce_s)166e28a4053SRui Paulo static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
167e28a4053SRui Paulo struct eap_sim_msg *msg, u16 counter,
168e28a4053SRui Paulo const u8 *nonce_s)
169e28a4053SRui Paulo {
170e28a4053SRui Paulo os_free(data->next_pseudonym);
171c1d255d3SCy Schubert if (!(sm->cfg->eap_sim_id & 0x01)) {
172206b73d0SCy Schubert /* Use of pseudonyms disabled in configuration */
173206b73d0SCy Schubert data->next_pseudonym = NULL;
174206b73d0SCy Schubert } else if (!nonce_s) {
175e28a4053SRui Paulo data->next_pseudonym =
176c1d255d3SCy Schubert eap_sim_db_get_next_pseudonym(sm->cfg->eap_sim_db_priv,
177f05cddf9SRui Paulo EAP_SIM_DB_SIM);
178f05cddf9SRui Paulo } else {
179f05cddf9SRui Paulo /* Do not update pseudonym during re-authentication */
180f05cddf9SRui Paulo data->next_pseudonym = NULL;
181f05cddf9SRui Paulo }
182e28a4053SRui Paulo os_free(data->next_reauth_id);
183c1d255d3SCy Schubert if (!(sm->cfg->eap_sim_id & 0x02)) {
184206b73d0SCy Schubert /* Use of fast reauth disabled in configuration */
185206b73d0SCy Schubert data->next_reauth_id = NULL;
186206b73d0SCy Schubert } else if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
187e28a4053SRui Paulo data->next_reauth_id =
188c1d255d3SCy Schubert eap_sim_db_get_next_reauth_id(sm->cfg->eap_sim_db_priv,
189f05cddf9SRui Paulo EAP_SIM_DB_SIM);
190e28a4053SRui Paulo } else {
191e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
192e28a4053SRui Paulo "count exceeded - force full authentication");
193e28a4053SRui Paulo data->next_reauth_id = NULL;
194e28a4053SRui Paulo }
195e28a4053SRui Paulo
196e28a4053SRui Paulo if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
197e28a4053SRui Paulo counter == 0 && nonce_s == NULL)
198e28a4053SRui Paulo return 0;
199e28a4053SRui Paulo
200e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_IV");
201e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
202e28a4053SRui Paulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
203e28a4053SRui Paulo
204e28a4053SRui Paulo if (counter > 0) {
205e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
206e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
207e28a4053SRui Paulo }
208e28a4053SRui Paulo
209e28a4053SRui Paulo if (nonce_s) {
210e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
211e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
212e28a4053SRui Paulo EAP_SIM_NONCE_S_LEN);
213e28a4053SRui Paulo }
214e28a4053SRui Paulo
215e28a4053SRui Paulo if (data->next_pseudonym) {
216e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
217e28a4053SRui Paulo data->next_pseudonym);
218e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
219e28a4053SRui Paulo os_strlen(data->next_pseudonym),
220e28a4053SRui Paulo (u8 *) data->next_pseudonym,
221e28a4053SRui Paulo os_strlen(data->next_pseudonym));
222e28a4053SRui Paulo }
223e28a4053SRui Paulo
224e28a4053SRui Paulo if (data->next_reauth_id) {
225e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
226e28a4053SRui Paulo data->next_reauth_id);
227e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
228e28a4053SRui Paulo os_strlen(data->next_reauth_id),
229e28a4053SRui Paulo (u8 *) data->next_reauth_id,
230e28a4053SRui Paulo os_strlen(data->next_reauth_id));
231e28a4053SRui Paulo }
232e28a4053SRui Paulo
233e28a4053SRui Paulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
234e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
235e28a4053SRui Paulo "AT_ENCR_DATA");
236e28a4053SRui Paulo return -1;
237e28a4053SRui Paulo }
238e28a4053SRui Paulo
239e28a4053SRui Paulo return 0;
240e28a4053SRui Paulo }
241e28a4053SRui Paulo
242e28a4053SRui Paulo
eap_sim_build_challenge(struct eap_sm * sm,struct eap_sim_data * data,u8 id)243e28a4053SRui Paulo static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
244e28a4053SRui Paulo struct eap_sim_data *data,
245e28a4053SRui Paulo u8 id)
246e28a4053SRui Paulo {
247e28a4053SRui Paulo struct eap_sim_msg *msg;
248e28a4053SRui Paulo
249e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
250e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
251e28a4053SRui Paulo EAP_SIM_SUBTYPE_CHALLENGE);
252e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RAND");
253e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
254e28a4053SRui Paulo data->num_chal * GSM_RAND_LEN);
255e28a4053SRui Paulo
256e28a4053SRui Paulo if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
257e28a4053SRui Paulo eap_sim_msg_free(msg);
258e28a4053SRui Paulo return NULL;
259e28a4053SRui Paulo }
260e28a4053SRui Paulo
261c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind) {
262e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
263e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
264e28a4053SRui Paulo }
265e28a4053SRui Paulo
266e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC");
267e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
2685b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
2695b9c547cSRui Paulo data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
270e28a4053SRui Paulo }
271e28a4053SRui Paulo
272e28a4053SRui Paulo
eap_sim_build_reauth(struct eap_sm * sm,struct eap_sim_data * data,u8 id)273e28a4053SRui Paulo static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
274e28a4053SRui Paulo struct eap_sim_data *data, u8 id)
275e28a4053SRui Paulo {
276e28a4053SRui Paulo struct eap_sim_msg *msg;
277206b73d0SCy Schubert struct wpabuf *buf;
278e28a4053SRui Paulo
279e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
280e28a4053SRui Paulo
281f05cddf9SRui Paulo if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
282e28a4053SRui Paulo return NULL;
283e28a4053SRui Paulo wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
284e28a4053SRui Paulo data->nonce_s, EAP_SIM_NONCE_S_LEN);
285e28a4053SRui Paulo
286e28a4053SRui Paulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
287e28a4053SRui Paulo data->emsk);
288e28a4053SRui Paulo eap_sim_derive_keys_reauth(data->counter, sm->identity,
289e28a4053SRui Paulo sm->identity_len, data->nonce_s, data->mk,
290e28a4053SRui Paulo data->msk, data->emsk);
291e28a4053SRui Paulo
292e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
293e28a4053SRui Paulo EAP_SIM_SUBTYPE_REAUTHENTICATION);
294e28a4053SRui Paulo
295e28a4053SRui Paulo if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
296e28a4053SRui Paulo eap_sim_msg_free(msg);
297e28a4053SRui Paulo return NULL;
298e28a4053SRui Paulo }
299e28a4053SRui Paulo
300c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind) {
301e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
302e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
303e28a4053SRui Paulo }
304e28a4053SRui Paulo
305e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC");
306e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
307206b73d0SCy Schubert buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
308206b73d0SCy Schubert
309206b73d0SCy Schubert /* Remember this MAC before sending it to the peer. This MAC is used for
310206b73d0SCy Schubert * Session-Id calculation after receiving response from the peer and
311206b73d0SCy Schubert * after all other checks pass. */
312206b73d0SCy Schubert os_memcpy(data->reauth_mac,
313206b73d0SCy Schubert wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
314206b73d0SCy Schubert EAP_SIM_MAC_LEN);
315206b73d0SCy Schubert
316206b73d0SCy Schubert return buf;
317e28a4053SRui Paulo }
318e28a4053SRui Paulo
319e28a4053SRui Paulo
eap_sim_build_notification(struct eap_sm * sm,struct eap_sim_data * data,u8 id)320e28a4053SRui Paulo static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
321e28a4053SRui Paulo struct eap_sim_data *data,
322e28a4053SRui Paulo u8 id)
323e28a4053SRui Paulo {
324e28a4053SRui Paulo struct eap_sim_msg *msg;
325e28a4053SRui Paulo
326e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
327e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
328e28a4053SRui Paulo EAP_SIM_SUBTYPE_NOTIFICATION);
329e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification);
330e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
331e28a4053SRui Paulo NULL, 0);
332e28a4053SRui Paulo if (data->use_result_ind) {
333e28a4053SRui Paulo if (data->reauth) {
334e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_IV");
335e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
336e28a4053SRui Paulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
337e28a4053SRui Paulo EAP_SIM_AT_ENCR_DATA);
338e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)",
339e28a4053SRui Paulo data->counter);
340e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
341e28a4053SRui Paulo NULL, 0);
342e28a4053SRui Paulo
343e28a4053SRui Paulo if (eap_sim_msg_add_encr_end(msg, data->k_encr,
344e28a4053SRui Paulo EAP_SIM_AT_PADDING)) {
345e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
346e28a4053SRui Paulo "encrypt AT_ENCR_DATA");
347e28a4053SRui Paulo eap_sim_msg_free(msg);
348e28a4053SRui Paulo return NULL;
349e28a4053SRui Paulo }
350e28a4053SRui Paulo }
351e28a4053SRui Paulo
352e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC");
353e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
354e28a4053SRui Paulo }
3555b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
356e28a4053SRui Paulo }
357e28a4053SRui Paulo
358e28a4053SRui Paulo
eap_sim_buildReq(struct eap_sm * sm,void * priv,u8 id)359e28a4053SRui Paulo static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
360e28a4053SRui Paulo {
361e28a4053SRui Paulo struct eap_sim_data *data = priv;
362e28a4053SRui Paulo
363e28a4053SRui Paulo switch (data->state) {
364e28a4053SRui Paulo case START:
365e28a4053SRui Paulo return eap_sim_build_start(sm, data, id);
366e28a4053SRui Paulo case CHALLENGE:
367e28a4053SRui Paulo return eap_sim_build_challenge(sm, data, id);
368e28a4053SRui Paulo case REAUTH:
369e28a4053SRui Paulo return eap_sim_build_reauth(sm, data, id);
370e28a4053SRui Paulo case NOTIFICATION:
371e28a4053SRui Paulo return eap_sim_build_notification(sm, data, id);
372e28a4053SRui Paulo default:
373e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
374e28a4053SRui Paulo "buildReq", data->state);
375e28a4053SRui Paulo break;
376e28a4053SRui Paulo }
377e28a4053SRui Paulo return NULL;
378e28a4053SRui Paulo }
379e28a4053SRui Paulo
380e28a4053SRui Paulo
eap_sim_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)381c1d255d3SCy Schubert static bool eap_sim_check(struct eap_sm *sm, void *priv,
382e28a4053SRui Paulo struct wpabuf *respData)
383e28a4053SRui Paulo {
384e28a4053SRui Paulo const u8 *pos;
385e28a4053SRui Paulo size_t len;
386e28a4053SRui Paulo
387e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
388e28a4053SRui Paulo if (pos == NULL || len < 3) {
389e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
390c1d255d3SCy Schubert return true;
391e28a4053SRui Paulo }
392e28a4053SRui Paulo
393c1d255d3SCy Schubert return false;
394f05cddf9SRui Paulo }
395f05cddf9SRui Paulo
396f05cddf9SRui Paulo
eap_sim_unexpected_subtype(struct eap_sim_data * data,u8 subtype)397c1d255d3SCy Schubert static bool eap_sim_unexpected_subtype(struct eap_sim_data *data,
398f05cddf9SRui Paulo u8 subtype)
399f05cddf9SRui Paulo {
400e28a4053SRui Paulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
401c1d255d3SCy Schubert return false;
402e28a4053SRui Paulo
403e28a4053SRui Paulo switch (data->state) {
404e28a4053SRui Paulo case START:
405e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_START) {
406e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
407e28a4053SRui Paulo "subtype %d", subtype);
408c1d255d3SCy Schubert return true;
409e28a4053SRui Paulo }
410e28a4053SRui Paulo break;
411e28a4053SRui Paulo case CHALLENGE:
412e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
413e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
414e28a4053SRui Paulo "subtype %d", subtype);
415c1d255d3SCy Schubert return true;
416e28a4053SRui Paulo }
417e28a4053SRui Paulo break;
418e28a4053SRui Paulo case REAUTH:
419e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
420e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
421e28a4053SRui Paulo "subtype %d", subtype);
422c1d255d3SCy Schubert return true;
423e28a4053SRui Paulo }
424e28a4053SRui Paulo break;
425e28a4053SRui Paulo case NOTIFICATION:
426e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
427e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
428e28a4053SRui Paulo "subtype %d", subtype);
429c1d255d3SCy Schubert return true;
430e28a4053SRui Paulo }
431e28a4053SRui Paulo break;
432e28a4053SRui Paulo default:
433e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
434e28a4053SRui Paulo "processing a response", data->state);
435c1d255d3SCy Schubert return true;
436e28a4053SRui Paulo }
437e28a4053SRui Paulo
438c1d255d3SCy Schubert return false;
439e28a4053SRui Paulo }
440e28a4053SRui Paulo
441e28a4053SRui Paulo
eap_sim_supported_ver(struct eap_sim_data * data,int version)442e28a4053SRui Paulo static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
443e28a4053SRui Paulo {
444e28a4053SRui Paulo return version == EAP_SIM_VERSION;
445e28a4053SRui Paulo }
446e28a4053SRui Paulo
447e28a4053SRui Paulo
eap_sim_process_start(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)448e28a4053SRui Paulo static void eap_sim_process_start(struct eap_sm *sm,
449e28a4053SRui Paulo struct eap_sim_data *data,
450e28a4053SRui Paulo struct wpabuf *respData,
451e28a4053SRui Paulo struct eap_sim_attrs *attr)
452e28a4053SRui Paulo {
453*a90b9d01SCy Schubert const u8 *identity;
454e28a4053SRui Paulo size_t identity_len;
455e28a4053SRui Paulo u8 ver_list[2];
456f05cddf9SRui Paulo u8 *new_identity;
457f05cddf9SRui Paulo char *username;
458e28a4053SRui Paulo
459e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
460e28a4053SRui Paulo
461f05cddf9SRui Paulo if (data->start_round == 0) {
462f05cddf9SRui Paulo /*
463f05cddf9SRui Paulo * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
464f05cddf9SRui Paulo * was requested since we already know it.
465f05cddf9SRui Paulo */
466f05cddf9SRui Paulo goto skip_id_update;
467f05cddf9SRui Paulo }
468f05cddf9SRui Paulo
469*a90b9d01SCy Schubert if ((sm->cfg->eap_sim_id & 0x04) &&
470*a90b9d01SCy Schubert (!attr->identity || attr->identity_len == 0))
471*a90b9d01SCy Schubert goto skip_id_attr;
472*a90b9d01SCy Schubert
473f05cddf9SRui Paulo /*
474*a90b9d01SCy Schubert * Unless explicitly configured otherwise, we always request identity
475*a90b9d01SCy Schubert * in SIM/Start, so the peer is required to have replied with one.
476f05cddf9SRui Paulo */
477f05cddf9SRui Paulo if (!attr->identity || attr->identity_len == 0) {
478f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
479f05cddf9SRui Paulo "identity");
480f05cddf9SRui Paulo goto failed;
481f05cddf9SRui Paulo }
482f05cddf9SRui Paulo
483f05cddf9SRui Paulo new_identity = os_malloc(attr->identity_len);
484f05cddf9SRui Paulo if (new_identity == NULL)
485f05cddf9SRui Paulo goto failed;
486e28a4053SRui Paulo os_free(sm->identity);
487f05cddf9SRui Paulo sm->identity = new_identity;
488f05cddf9SRui Paulo os_memcpy(sm->identity, attr->identity, attr->identity_len);
489e28a4053SRui Paulo sm->identity_len = attr->identity_len;
490e28a4053SRui Paulo
491*a90b9d01SCy Schubert skip_id_attr:
492*a90b9d01SCy Schubert if (sm->sim_aka_permanent[0]) {
493*a90b9d01SCy Schubert identity = (const u8 *) sm->sim_aka_permanent;
494*a90b9d01SCy Schubert identity_len = os_strlen(sm->sim_aka_permanent);
495*a90b9d01SCy Schubert } else {
496*a90b9d01SCy Schubert identity = sm->identity;
497*a90b9d01SCy Schubert identity_len = sm->identity_len;
498*a90b9d01SCy Schubert }
499e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
500*a90b9d01SCy Schubert identity, identity_len);
501*a90b9d01SCy Schubert username = sim_get_username(identity, identity_len);
502f05cddf9SRui Paulo if (username == NULL)
503f05cddf9SRui Paulo goto failed;
504e28a4053SRui Paulo
505f05cddf9SRui Paulo if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
506f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
507f05cddf9SRui Paulo username);
508f05cddf9SRui Paulo data->reauth = eap_sim_db_get_reauth_entry(
509c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, username);
510f05cddf9SRui Paulo os_free(username);
511f05cddf9SRui Paulo if (data->reauth == NULL) {
512f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
513f05cddf9SRui Paulo "identity - request full auth identity");
514f05cddf9SRui Paulo /* Remain in START state for another round */
515f05cddf9SRui Paulo return;
516f05cddf9SRui Paulo }
517*a90b9d01SCy Schubert
518*a90b9d01SCy Schubert if (data->reauth->counter >
519*a90b9d01SCy Schubert sm->cfg->eap_sim_aka_fast_reauth_limit &&
520*a90b9d01SCy Schubert (sm->cfg->eap_sim_id & 0x04)) {
521*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
522*a90b9d01SCy Schubert "EAP-SIM: Too many fast re-authentication attemps - fall back to full authentication");
523*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
524*a90b9d01SCy Schubert "EAP-SIM: Permanent identity recognized - skip new Identity query");
525*a90b9d01SCy Schubert os_strlcpy(data->permanent,
526*a90b9d01SCy Schubert data->reauth->permanent,
527*a90b9d01SCy Schubert sizeof(data->permanent));
528*a90b9d01SCy Schubert os_strlcpy(sm->sim_aka_permanent,
529*a90b9d01SCy Schubert data->reauth->permanent,
530*a90b9d01SCy Schubert sizeof(sm->sim_aka_permanent));
531*a90b9d01SCy Schubert eap_sim_db_remove_reauth(
532*a90b9d01SCy Schubert sm->cfg->eap_sim_db_priv,
533*a90b9d01SCy Schubert data->reauth);
534*a90b9d01SCy Schubert data->reauth = NULL;
535*a90b9d01SCy Schubert goto skip_id_update;
536*a90b9d01SCy Schubert }
537*a90b9d01SCy Schubert
538*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
539*a90b9d01SCy Schubert "EAP-SIM: Using fast re-authentication (counter=%d)",
540*a90b9d01SCy Schubert data->reauth->counter);
541f05cddf9SRui Paulo os_strlcpy(data->permanent, data->reauth->permanent,
542f05cddf9SRui Paulo sizeof(data->permanent));
543f05cddf9SRui Paulo data->counter = data->reauth->counter;
544f05cddf9SRui Paulo os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
545e28a4053SRui Paulo eap_sim_state(data, REAUTH);
546e28a4053SRui Paulo return;
547e28a4053SRui Paulo }
548e28a4053SRui Paulo
549f05cddf9SRui Paulo if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
550f05cddf9SRui Paulo const char *permanent;
551f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
552f05cddf9SRui Paulo username);
553f05cddf9SRui Paulo permanent = eap_sim_db_get_permanent(
554c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, username);
555f05cddf9SRui Paulo os_free(username);
556f05cddf9SRui Paulo if (permanent == NULL) {
557f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
558f05cddf9SRui Paulo "identity - request permanent identity");
559f05cddf9SRui Paulo /* Remain in START state for another round */
560f05cddf9SRui Paulo return;
561f05cddf9SRui Paulo }
562f05cddf9SRui Paulo os_strlcpy(data->permanent, permanent,
563f05cddf9SRui Paulo sizeof(data->permanent));
564f05cddf9SRui Paulo } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
565f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
566f05cddf9SRui Paulo username);
567f05cddf9SRui Paulo os_strlcpy(data->permanent, username, sizeof(data->permanent));
568f05cddf9SRui Paulo os_free(username);
569*a90b9d01SCy Schubert #ifdef CRYPTO_RSA_OAEP_SHA256
570*a90b9d01SCy Schubert } else if (sm->identity_len > 1 && sm->identity[0] == '\0') {
571*a90b9d01SCy Schubert char *enc_id, *pos, *end;
572*a90b9d01SCy Schubert size_t enc_id_len;
573*a90b9d01SCy Schubert u8 *decoded_id;
574*a90b9d01SCy Schubert size_t decoded_id_len;
575*a90b9d01SCy Schubert struct wpabuf *enc, *dec;
576*a90b9d01SCy Schubert u8 *new_id;
577*a90b9d01SCy Schubert
578*a90b9d01SCy Schubert os_free(username);
579*a90b9d01SCy Schubert if (!sm->cfg->imsi_privacy_key) {
580*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
581*a90b9d01SCy Schubert "EAP-SIM: Received encrypted identity, but no IMSI privacy key configured to decrypt it");
582*a90b9d01SCy Schubert goto failed;
583*a90b9d01SCy Schubert }
584*a90b9d01SCy Schubert
585*a90b9d01SCy Schubert enc_id = (char *) &sm->identity[1];
586*a90b9d01SCy Schubert end = (char *) &sm->identity[sm->identity_len];
587*a90b9d01SCy Schubert for (pos = enc_id; pos < end; pos++) {
588*a90b9d01SCy Schubert if (*pos == ',')
589*a90b9d01SCy Schubert break;
590*a90b9d01SCy Schubert }
591*a90b9d01SCy Schubert enc_id_len = pos - enc_id;
592*a90b9d01SCy Schubert
593*a90b9d01SCy Schubert wpa_hexdump_ascii(MSG_DEBUG,
594*a90b9d01SCy Schubert "EAP-SIM: Encrypted permanent identity",
595*a90b9d01SCy Schubert enc_id, enc_id_len);
596*a90b9d01SCy Schubert decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len);
597*a90b9d01SCy Schubert if (!decoded_id) {
598*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
599*a90b9d01SCy Schubert "EAP-SIM: Could not base64 decode encrypted identity");
600*a90b9d01SCy Schubert goto failed;
601*a90b9d01SCy Schubert }
602*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG,
603*a90b9d01SCy Schubert "EAP-SIM: Decoded encrypted permanent identity",
604*a90b9d01SCy Schubert decoded_id, decoded_id_len);
605*a90b9d01SCy Schubert enc = wpabuf_alloc_copy(decoded_id, decoded_id_len);
606*a90b9d01SCy Schubert os_free(decoded_id);
607*a90b9d01SCy Schubert if (!enc)
608*a90b9d01SCy Schubert goto failed;
609*a90b9d01SCy Schubert dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key,
610*a90b9d01SCy Schubert enc);
611*a90b9d01SCy Schubert wpabuf_free(enc);
612*a90b9d01SCy Schubert if (!dec) {
613*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
614*a90b9d01SCy Schubert "EAP-SIM: Failed to decrypt encrypted identity");
615*a90b9d01SCy Schubert goto failed;
616*a90b9d01SCy Schubert }
617*a90b9d01SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Decrypted permanent identity",
618*a90b9d01SCy Schubert wpabuf_head(dec), wpabuf_len(dec));
619*a90b9d01SCy Schubert username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec));
620*a90b9d01SCy Schubert if (!username) {
621*a90b9d01SCy Schubert wpabuf_free(dec);
622*a90b9d01SCy Schubert goto failed;
623*a90b9d01SCy Schubert }
624*a90b9d01SCy Schubert new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec));
625*a90b9d01SCy Schubert if (!new_id) {
626*a90b9d01SCy Schubert wpabuf_free(dec);
627*a90b9d01SCy Schubert goto failed;
628*a90b9d01SCy Schubert }
629*a90b9d01SCy Schubert os_free(sm->identity);
630*a90b9d01SCy Schubert sm->identity = new_id;
631*a90b9d01SCy Schubert sm->identity_len = wpabuf_len(dec);
632*a90b9d01SCy Schubert wpabuf_free(dec);
633*a90b9d01SCy Schubert os_strlcpy(data->permanent, username, sizeof(data->permanent));
634*a90b9d01SCy Schubert os_free(username);
635*a90b9d01SCy Schubert #endif /* CRYPTO_RSA_OAEP_SHA256 */
636f05cddf9SRui Paulo } else {
637f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
638f05cddf9SRui Paulo username);
639f05cddf9SRui Paulo os_free(username);
640f05cddf9SRui Paulo goto failed;
641f05cddf9SRui Paulo }
642f05cddf9SRui Paulo
643f05cddf9SRui Paulo skip_id_update:
644f05cddf9SRui Paulo /* Full authentication */
645f05cddf9SRui Paulo
646e28a4053SRui Paulo if (attr->nonce_mt == NULL || attr->selected_version < 0) {
647e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
648e28a4053SRui Paulo "required attributes");
649f05cddf9SRui Paulo goto failed;
650e28a4053SRui Paulo }
651e28a4053SRui Paulo
652e28a4053SRui Paulo if (!eap_sim_supported_ver(data, attr->selected_version)) {
653e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
654e28a4053SRui Paulo "version %d", attr->selected_version);
655f05cddf9SRui Paulo goto failed;
656e28a4053SRui Paulo }
657e28a4053SRui Paulo
658e28a4053SRui Paulo data->counter = 0; /* reset re-auth counter since this is full auth */
659e28a4053SRui Paulo data->reauth = NULL;
660e28a4053SRui Paulo
661e28a4053SRui Paulo data->num_chal = eap_sim_db_get_gsm_triplets(
662c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
663e28a4053SRui Paulo (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
664e28a4053SRui Paulo if (data->num_chal == EAP_SIM_DB_PENDING) {
665e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
666e28a4053SRui Paulo "not yet available - pending request");
667e28a4053SRui Paulo sm->method_pending = METHOD_PENDING_WAIT;
668e28a4053SRui Paulo return;
669e28a4053SRui Paulo }
670e28a4053SRui Paulo if (data->num_chal < 2) {
671e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
672e28a4053SRui Paulo "authentication triplets for the peer");
673f05cddf9SRui Paulo goto failed;
674e28a4053SRui Paulo }
675e28a4053SRui Paulo
6764bc52338SCy Schubert if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX)
6774bc52338SCy Schubert os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi));
6784bc52338SCy Schubert
679e28a4053SRui Paulo identity_len = sm->identity_len;
680e28a4053SRui Paulo while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
681e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
682e28a4053SRui Paulo "character from identity");
683e28a4053SRui Paulo identity_len--;
684e28a4053SRui Paulo }
685e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
686e28a4053SRui Paulo sm->identity, identity_len);
687e28a4053SRui Paulo
688e28a4053SRui Paulo os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
689e28a4053SRui Paulo WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
690e28a4053SRui Paulo eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
691e28a4053SRui Paulo attr->selected_version, ver_list, sizeof(ver_list),
692e28a4053SRui Paulo data->num_chal, (const u8 *) data->kc, data->mk);
693e28a4053SRui Paulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
694e28a4053SRui Paulo data->emsk);
695e28a4053SRui Paulo
696e28a4053SRui Paulo eap_sim_state(data, CHALLENGE);
697f05cddf9SRui Paulo return;
698f05cddf9SRui Paulo
699f05cddf9SRui Paulo failed:
700f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
701f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION);
702e28a4053SRui Paulo }
703e28a4053SRui Paulo
704e28a4053SRui Paulo
eap_sim_process_challenge(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)705e28a4053SRui Paulo static void eap_sim_process_challenge(struct eap_sm *sm,
706e28a4053SRui Paulo struct eap_sim_data *data,
707e28a4053SRui Paulo struct wpabuf *respData,
708e28a4053SRui Paulo struct eap_sim_attrs *attr)
709e28a4053SRui Paulo {
710e28a4053SRui Paulo if (attr->mac == NULL ||
711e28a4053SRui Paulo eap_sim_verify_mac(data->k_aut, respData, attr->mac,
712e28a4053SRui Paulo (u8 *) data->sres,
713e28a4053SRui Paulo data->num_chal * EAP_SIM_SRES_LEN)) {
714e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
715e28a4053SRui Paulo "did not include valid AT_MAC");
716f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
717f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION);
718e28a4053SRui Paulo return;
719e28a4053SRui Paulo }
720e28a4053SRui Paulo
721e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
722e28a4053SRui Paulo "correct AT_MAC");
723c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
724e28a4053SRui Paulo data->use_result_ind = 1;
725e28a4053SRui Paulo data->notification = EAP_SIM_SUCCESS;
726e28a4053SRui Paulo eap_sim_state(data, NOTIFICATION);
727e28a4053SRui Paulo } else
728e28a4053SRui Paulo eap_sim_state(data, SUCCESS);
729e28a4053SRui Paulo
730e28a4053SRui Paulo if (data->next_pseudonym) {
731c1d255d3SCy Schubert eap_sim_db_add_pseudonym(sm->cfg->eap_sim_db_priv,
732c1d255d3SCy Schubert data->permanent,
733e28a4053SRui Paulo data->next_pseudonym);
734e28a4053SRui Paulo data->next_pseudonym = NULL;
735e28a4053SRui Paulo }
736e28a4053SRui Paulo if (data->next_reauth_id) {
737c1d255d3SCy Schubert eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent,
738e28a4053SRui Paulo data->next_reauth_id, data->counter + 1,
739e28a4053SRui Paulo data->mk);
740e28a4053SRui Paulo data->next_reauth_id = NULL;
741e28a4053SRui Paulo }
742e28a4053SRui Paulo }
743e28a4053SRui Paulo
744e28a4053SRui Paulo
eap_sim_process_reauth(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)745e28a4053SRui Paulo static void eap_sim_process_reauth(struct eap_sm *sm,
746e28a4053SRui Paulo struct eap_sim_data *data,
747e28a4053SRui Paulo struct wpabuf *respData,
748e28a4053SRui Paulo struct eap_sim_attrs *attr)
749e28a4053SRui Paulo {
750e28a4053SRui Paulo struct eap_sim_attrs eattr;
751e28a4053SRui Paulo u8 *decrypted = NULL;
752e28a4053SRui Paulo
753e28a4053SRui Paulo if (attr->mac == NULL ||
754e28a4053SRui Paulo eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
755e28a4053SRui Paulo EAP_SIM_NONCE_S_LEN)) {
756e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
757e28a4053SRui Paulo "did not include valid AT_MAC");
758e28a4053SRui Paulo goto fail;
759e28a4053SRui Paulo }
760e28a4053SRui Paulo
761e28a4053SRui Paulo if (attr->encr_data == NULL || attr->iv == NULL) {
762e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
763e28a4053SRui Paulo "message did not include encrypted data");
764e28a4053SRui Paulo goto fail;
765e28a4053SRui Paulo }
766e28a4053SRui Paulo
767e28a4053SRui Paulo decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
768e28a4053SRui Paulo attr->encr_data_len, attr->iv, &eattr,
769e28a4053SRui Paulo 0);
770e28a4053SRui Paulo if (decrypted == NULL) {
771e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
772e28a4053SRui Paulo "data from reauthentication message");
773e28a4053SRui Paulo goto fail;
774e28a4053SRui Paulo }
775e28a4053SRui Paulo
776e28a4053SRui Paulo if (eattr.counter != data->counter) {
777e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
778e28a4053SRui Paulo "used incorrect counter %u, expected %u",
779e28a4053SRui Paulo eattr.counter, data->counter);
780e28a4053SRui Paulo goto fail;
781e28a4053SRui Paulo }
782e28a4053SRui Paulo os_free(decrypted);
783e28a4053SRui Paulo decrypted = NULL;
784e28a4053SRui Paulo
785e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
786e28a4053SRui Paulo "the correct AT_MAC");
787f05cddf9SRui Paulo
788f05cddf9SRui Paulo if (eattr.counter_too_small) {
789f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
790f05cddf9SRui Paulo "included AT_COUNTER_TOO_SMALL - starting full "
791f05cddf9SRui Paulo "authentication");
792f05cddf9SRui Paulo data->start_round = -1;
793f05cddf9SRui Paulo eap_sim_state(data, START);
794f05cddf9SRui Paulo return;
795f05cddf9SRui Paulo }
796f05cddf9SRui Paulo
797c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
798e28a4053SRui Paulo data->use_result_ind = 1;
799e28a4053SRui Paulo data->notification = EAP_SIM_SUCCESS;
800e28a4053SRui Paulo eap_sim_state(data, NOTIFICATION);
801e28a4053SRui Paulo } else
802e28a4053SRui Paulo eap_sim_state(data, SUCCESS);
803e28a4053SRui Paulo
804e28a4053SRui Paulo if (data->next_reauth_id) {
805c1d255d3SCy Schubert eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent,
806f05cddf9SRui Paulo data->next_reauth_id,
807e28a4053SRui Paulo data->counter + 1, data->mk);
808e28a4053SRui Paulo data->next_reauth_id = NULL;
809e28a4053SRui Paulo } else {
810c1d255d3SCy Schubert eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
811c1d255d3SCy Schubert data->reauth);
812e28a4053SRui Paulo data->reauth = NULL;
813e28a4053SRui Paulo }
814e28a4053SRui Paulo
815e28a4053SRui Paulo return;
816e28a4053SRui Paulo
817e28a4053SRui Paulo fail:
818f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
819f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION);
820c1d255d3SCy Schubert eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, data->reauth);
821e28a4053SRui Paulo data->reauth = NULL;
822e28a4053SRui Paulo os_free(decrypted);
823e28a4053SRui Paulo }
824e28a4053SRui Paulo
825e28a4053SRui Paulo
eap_sim_process_client_error(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)826e28a4053SRui Paulo static void eap_sim_process_client_error(struct eap_sm *sm,
827e28a4053SRui Paulo struct eap_sim_data *data,
828e28a4053SRui Paulo struct wpabuf *respData,
829e28a4053SRui Paulo struct eap_sim_attrs *attr)
830e28a4053SRui Paulo {
831e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
832e28a4053SRui Paulo attr->client_error_code);
833e28a4053SRui Paulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
834e28a4053SRui Paulo eap_sim_state(data, SUCCESS);
835e28a4053SRui Paulo else
836e28a4053SRui Paulo eap_sim_state(data, FAILURE);
837e28a4053SRui Paulo }
838e28a4053SRui Paulo
839e28a4053SRui Paulo
eap_sim_process_notification(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)840e28a4053SRui Paulo static void eap_sim_process_notification(struct eap_sm *sm,
841e28a4053SRui Paulo struct eap_sim_data *data,
842e28a4053SRui Paulo struct wpabuf *respData,
843e28a4053SRui Paulo struct eap_sim_attrs *attr)
844e28a4053SRui Paulo {
845e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
846e28a4053SRui Paulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
847e28a4053SRui Paulo eap_sim_state(data, SUCCESS);
848e28a4053SRui Paulo else
849e28a4053SRui Paulo eap_sim_state(data, FAILURE);
850e28a4053SRui Paulo }
851e28a4053SRui Paulo
852e28a4053SRui Paulo
eap_sim_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)853e28a4053SRui Paulo static void eap_sim_process(struct eap_sm *sm, void *priv,
854e28a4053SRui Paulo struct wpabuf *respData)
855e28a4053SRui Paulo {
856e28a4053SRui Paulo struct eap_sim_data *data = priv;
857e28a4053SRui Paulo const u8 *pos, *end;
858e28a4053SRui Paulo u8 subtype;
859e28a4053SRui Paulo size_t len;
860e28a4053SRui Paulo struct eap_sim_attrs attr;
861e28a4053SRui Paulo
862e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
863e28a4053SRui Paulo if (pos == NULL || len < 3)
864e28a4053SRui Paulo return;
865e28a4053SRui Paulo
866e28a4053SRui Paulo end = pos + len;
867e28a4053SRui Paulo subtype = *pos;
868e28a4053SRui Paulo pos += 3;
869e28a4053SRui Paulo
870f05cddf9SRui Paulo if (eap_sim_unexpected_subtype(data, subtype)) {
871f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
872f05cddf9SRui Paulo "EAP-SIM Subtype in EAP Response");
873f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
874f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION);
875f05cddf9SRui Paulo return;
876f05cddf9SRui Paulo }
877f05cddf9SRui Paulo
878e28a4053SRui Paulo if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
879e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
880f05cddf9SRui Paulo if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
881f05cddf9SRui Paulo (data->state == START || data->state == CHALLENGE ||
882f05cddf9SRui Paulo data->state == REAUTH)) {
883f05cddf9SRui Paulo data->notification =
884f05cddf9SRui Paulo EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
885f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION);
886f05cddf9SRui Paulo return;
887f05cddf9SRui Paulo }
888e28a4053SRui Paulo eap_sim_state(data, FAILURE);
889e28a4053SRui Paulo return;
890e28a4053SRui Paulo }
891e28a4053SRui Paulo
892e28a4053SRui Paulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
893e28a4053SRui Paulo eap_sim_process_client_error(sm, data, respData, &attr);
894e28a4053SRui Paulo return;
895e28a4053SRui Paulo }
896e28a4053SRui Paulo
897e28a4053SRui Paulo switch (data->state) {
898e28a4053SRui Paulo case START:
899e28a4053SRui Paulo eap_sim_process_start(sm, data, respData, &attr);
900e28a4053SRui Paulo break;
901e28a4053SRui Paulo case CHALLENGE:
902e28a4053SRui Paulo eap_sim_process_challenge(sm, data, respData, &attr);
903e28a4053SRui Paulo break;
904e28a4053SRui Paulo case REAUTH:
905e28a4053SRui Paulo eap_sim_process_reauth(sm, data, respData, &attr);
906e28a4053SRui Paulo break;
907e28a4053SRui Paulo case NOTIFICATION:
908e28a4053SRui Paulo eap_sim_process_notification(sm, data, respData, &attr);
909e28a4053SRui Paulo break;
910e28a4053SRui Paulo default:
911e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
912e28a4053SRui Paulo "process", data->state);
913e28a4053SRui Paulo break;
914e28a4053SRui Paulo }
915e28a4053SRui Paulo }
916e28a4053SRui Paulo
917e28a4053SRui Paulo
eap_sim_isDone(struct eap_sm * sm,void * priv)918c1d255d3SCy Schubert static bool eap_sim_isDone(struct eap_sm *sm, void *priv)
919e28a4053SRui Paulo {
920e28a4053SRui Paulo struct eap_sim_data *data = priv;
921e28a4053SRui Paulo return data->state == SUCCESS || data->state == FAILURE;
922e28a4053SRui Paulo }
923e28a4053SRui Paulo
924e28a4053SRui Paulo
eap_sim_getKey(struct eap_sm * sm,void * priv,size_t * len)925e28a4053SRui Paulo static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
926e28a4053SRui Paulo {
927e28a4053SRui Paulo struct eap_sim_data *data = priv;
928e28a4053SRui Paulo u8 *key;
929e28a4053SRui Paulo
930e28a4053SRui Paulo if (data->state != SUCCESS)
931e28a4053SRui Paulo return NULL;
932e28a4053SRui Paulo
93385732ac8SCy Schubert key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
934e28a4053SRui Paulo if (key == NULL)
935e28a4053SRui Paulo return NULL;
936e28a4053SRui Paulo *len = EAP_SIM_KEYING_DATA_LEN;
937e28a4053SRui Paulo return key;
938e28a4053SRui Paulo }
939e28a4053SRui Paulo
940e28a4053SRui Paulo
eap_sim_get_emsk(struct eap_sm * sm,void * priv,size_t * len)941e28a4053SRui Paulo static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
942e28a4053SRui Paulo {
943e28a4053SRui Paulo struct eap_sim_data *data = priv;
944e28a4053SRui Paulo u8 *key;
945e28a4053SRui Paulo
946e28a4053SRui Paulo if (data->state != SUCCESS)
947e28a4053SRui Paulo return NULL;
948e28a4053SRui Paulo
94985732ac8SCy Schubert key = os_memdup(data->emsk, EAP_EMSK_LEN);
950e28a4053SRui Paulo if (key == NULL)
951e28a4053SRui Paulo return NULL;
952e28a4053SRui Paulo *len = EAP_EMSK_LEN;
953e28a4053SRui Paulo return key;
954e28a4053SRui Paulo }
955e28a4053SRui Paulo
956e28a4053SRui Paulo
eap_sim_isSuccess(struct eap_sm * sm,void * priv)957c1d255d3SCy Schubert static bool eap_sim_isSuccess(struct eap_sm *sm, void *priv)
958e28a4053SRui Paulo {
959e28a4053SRui Paulo struct eap_sim_data *data = priv;
960e28a4053SRui Paulo return data->state == SUCCESS;
961e28a4053SRui Paulo }
962e28a4053SRui Paulo
963e28a4053SRui Paulo
eap_sim_get_session_id(struct eap_sm * sm,void * priv,size_t * len)9645b9c547cSRui Paulo static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
9655b9c547cSRui Paulo {
9665b9c547cSRui Paulo struct eap_sim_data *data = priv;
9675b9c547cSRui Paulo u8 *id;
9685b9c547cSRui Paulo
9695b9c547cSRui Paulo if (data->state != SUCCESS)
9705b9c547cSRui Paulo return NULL;
9715b9c547cSRui Paulo
972206b73d0SCy Schubert if (!data->reauth)
9735b9c547cSRui Paulo *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
974206b73d0SCy Schubert else
975206b73d0SCy Schubert *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
9765b9c547cSRui Paulo id = os_malloc(*len);
9775b9c547cSRui Paulo if (id == NULL)
9785b9c547cSRui Paulo return NULL;
9795b9c547cSRui Paulo
9805b9c547cSRui Paulo id[0] = EAP_TYPE_SIM;
981206b73d0SCy Schubert if (!data->reauth) {
9825b9c547cSRui Paulo os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
983206b73d0SCy Schubert os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN,
984206b73d0SCy Schubert data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
985206b73d0SCy Schubert } else {
986206b73d0SCy Schubert os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
987206b73d0SCy Schubert os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
988206b73d0SCy Schubert EAP_SIM_MAC_LEN);
989206b73d0SCy Schubert
990206b73d0SCy Schubert }
9915b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
9925b9c547cSRui Paulo
9935b9c547cSRui Paulo return id;
9945b9c547cSRui Paulo }
9955b9c547cSRui Paulo
9965b9c547cSRui Paulo
eap_server_sim_register(void)997e28a4053SRui Paulo int eap_server_sim_register(void)
998e28a4053SRui Paulo {
999e28a4053SRui Paulo struct eap_method *eap;
1000e28a4053SRui Paulo
1001e28a4053SRui Paulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
1002e28a4053SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
1003e28a4053SRui Paulo if (eap == NULL)
1004e28a4053SRui Paulo return -1;
1005e28a4053SRui Paulo
1006e28a4053SRui Paulo eap->init = eap_sim_init;
1007e28a4053SRui Paulo eap->reset = eap_sim_reset;
1008e28a4053SRui Paulo eap->buildReq = eap_sim_buildReq;
1009e28a4053SRui Paulo eap->check = eap_sim_check;
1010e28a4053SRui Paulo eap->process = eap_sim_process;
1011e28a4053SRui Paulo eap->isDone = eap_sim_isDone;
1012e28a4053SRui Paulo eap->getKey = eap_sim_getKey;
1013e28a4053SRui Paulo eap->isSuccess = eap_sim_isSuccess;
1014e28a4053SRui Paulo eap->get_emsk = eap_sim_get_emsk;
10155b9c547cSRui Paulo eap->getSessionId = eap_sim_get_session_id;
1016e28a4053SRui Paulo
1017780fb4a2SCy Schubert return eap_server_method_register(eap);
1018e28a4053SRui Paulo }
1019