13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * hostapd / EAP-SIM (RFC 4186)
33ff40c12SJohn Marino * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino */
83ff40c12SJohn Marino
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "crypto/random.h"
133ff40c12SJohn Marino #include "eap_server/eap_i.h"
143ff40c12SJohn Marino #include "eap_common/eap_sim_common.h"
153ff40c12SJohn Marino #include "eap_server/eap_sim_db.h"
163ff40c12SJohn Marino
173ff40c12SJohn Marino
183ff40c12SJohn Marino struct eap_sim_data {
193ff40c12SJohn Marino u8 mk[EAP_SIM_MK_LEN];
203ff40c12SJohn Marino u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
213ff40c12SJohn Marino u8 nonce_s[EAP_SIM_NONCE_S_LEN];
223ff40c12SJohn Marino u8 k_aut[EAP_SIM_K_AUT_LEN];
233ff40c12SJohn Marino u8 k_encr[EAP_SIM_K_ENCR_LEN];
243ff40c12SJohn Marino u8 msk[EAP_SIM_KEYING_DATA_LEN];
253ff40c12SJohn Marino u8 emsk[EAP_EMSK_LEN];
263ff40c12SJohn Marino u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
273ff40c12SJohn Marino u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
283ff40c12SJohn Marino u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
29*a1157835SDaniel Fojt u8 reauth_mac[EAP_SIM_MAC_LEN];
303ff40c12SJohn Marino int num_chal;
313ff40c12SJohn Marino enum {
323ff40c12SJohn Marino START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
333ff40c12SJohn Marino } state;
343ff40c12SJohn Marino char *next_pseudonym;
353ff40c12SJohn Marino char *next_reauth_id;
363ff40c12SJohn Marino u16 counter;
373ff40c12SJohn Marino struct eap_sim_reauth *reauth;
383ff40c12SJohn Marino u16 notification;
393ff40c12SJohn Marino int use_result_ind;
403ff40c12SJohn Marino int start_round;
413ff40c12SJohn Marino char permanent[20]; /* Permanent username */
423ff40c12SJohn Marino };
433ff40c12SJohn Marino
443ff40c12SJohn Marino
eap_sim_state_txt(int state)453ff40c12SJohn Marino static const char * eap_sim_state_txt(int state)
463ff40c12SJohn Marino {
473ff40c12SJohn Marino switch (state) {
483ff40c12SJohn Marino case START:
493ff40c12SJohn Marino return "START";
503ff40c12SJohn Marino case CHALLENGE:
513ff40c12SJohn Marino return "CHALLENGE";
523ff40c12SJohn Marino case REAUTH:
533ff40c12SJohn Marino return "REAUTH";
543ff40c12SJohn Marino case SUCCESS:
553ff40c12SJohn Marino return "SUCCESS";
563ff40c12SJohn Marino case FAILURE:
573ff40c12SJohn Marino return "FAILURE";
583ff40c12SJohn Marino case NOTIFICATION:
593ff40c12SJohn Marino return "NOTIFICATION";
603ff40c12SJohn Marino default:
613ff40c12SJohn Marino return "Unknown?!";
623ff40c12SJohn Marino }
633ff40c12SJohn Marino }
643ff40c12SJohn Marino
653ff40c12SJohn Marino
eap_sim_state(struct eap_sim_data * data,int state)663ff40c12SJohn Marino static void eap_sim_state(struct eap_sim_data *data, int state)
673ff40c12SJohn Marino {
683ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
693ff40c12SJohn Marino eap_sim_state_txt(data->state),
703ff40c12SJohn Marino eap_sim_state_txt(state));
713ff40c12SJohn Marino data->state = state;
723ff40c12SJohn Marino }
733ff40c12SJohn Marino
743ff40c12SJohn Marino
eap_sim_init(struct eap_sm * sm)753ff40c12SJohn Marino static void * eap_sim_init(struct eap_sm *sm)
763ff40c12SJohn Marino {
773ff40c12SJohn Marino struct eap_sim_data *data;
783ff40c12SJohn Marino
793ff40c12SJohn Marino if (sm->eap_sim_db_priv == NULL) {
803ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
813ff40c12SJohn Marino return NULL;
823ff40c12SJohn Marino }
833ff40c12SJohn Marino
843ff40c12SJohn Marino data = os_zalloc(sizeof(*data));
853ff40c12SJohn Marino if (data == NULL)
863ff40c12SJohn Marino return NULL;
873ff40c12SJohn Marino data->state = START;
883ff40c12SJohn Marino
893ff40c12SJohn Marino return data;
903ff40c12SJohn Marino }
913ff40c12SJohn Marino
923ff40c12SJohn Marino
eap_sim_reset(struct eap_sm * sm,void * priv)933ff40c12SJohn Marino static void eap_sim_reset(struct eap_sm *sm, void *priv)
943ff40c12SJohn Marino {
953ff40c12SJohn Marino struct eap_sim_data *data = priv;
963ff40c12SJohn Marino os_free(data->next_pseudonym);
973ff40c12SJohn Marino os_free(data->next_reauth_id);
98*a1157835SDaniel Fojt bin_clear_free(data, sizeof(*data));
993ff40c12SJohn Marino }
1003ff40c12SJohn Marino
1013ff40c12SJohn Marino
eap_sim_build_start(struct eap_sm * sm,struct eap_sim_data * data,u8 id)1023ff40c12SJohn Marino static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
1033ff40c12SJohn Marino struct eap_sim_data *data, u8 id)
1043ff40c12SJohn Marino {
1053ff40c12SJohn Marino struct eap_sim_msg *msg;
1063ff40c12SJohn Marino u8 ver[2];
1073ff40c12SJohn Marino
1083ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
1093ff40c12SJohn Marino msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
1103ff40c12SJohn Marino EAP_SIM_SUBTYPE_START);
1113ff40c12SJohn Marino data->start_round++;
1123ff40c12SJohn Marino if (data->start_round == 1) {
1133ff40c12SJohn Marino /*
1143ff40c12SJohn Marino * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
1153ff40c12SJohn Marino * ignored and the SIM/Start is used to request the identity.
1163ff40c12SJohn Marino */
1173ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
1183ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
1193ff40c12SJohn Marino } else if (data->start_round > 3) {
1203ff40c12SJohn Marino /* Cannot use more than three rounds of Start messages */
1213ff40c12SJohn Marino eap_sim_msg_free(msg);
1223ff40c12SJohn Marino return NULL;
1233ff40c12SJohn Marino } else if (data->start_round == 0) {
1243ff40c12SJohn Marino /*
1253ff40c12SJohn Marino * This is a special case that is used to recover from
1263ff40c12SJohn Marino * AT_COUNTER_TOO_SMALL during re-authentication. Since we
1273ff40c12SJohn Marino * already know the identity of the peer, there is no need to
1283ff40c12SJohn Marino * request any identity in this case.
1293ff40c12SJohn Marino */
1303ff40c12SJohn Marino } else if (sm->identity && sm->identity_len > 0 &&
1313ff40c12SJohn Marino sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
1323ff40c12SJohn Marino /* Reauth id may have expired - try fullauth */
1333ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ");
1343ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
1353ff40c12SJohn Marino } else {
1363ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
1373ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
1383ff40c12SJohn Marino }
1393ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
1403ff40c12SJohn Marino ver[0] = 0;
1413ff40c12SJohn Marino ver[1] = EAP_SIM_VERSION;
1423ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
1433ff40c12SJohn Marino ver, sizeof(ver));
144*a1157835SDaniel Fojt return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
1453ff40c12SJohn Marino }
1463ff40c12SJohn Marino
1473ff40c12SJohn Marino
eap_sim_build_encr(struct eap_sm * sm,struct eap_sim_data * data,struct eap_sim_msg * msg,u16 counter,const u8 * nonce_s)1483ff40c12SJohn Marino static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
1493ff40c12SJohn Marino struct eap_sim_msg *msg, u16 counter,
1503ff40c12SJohn Marino const u8 *nonce_s)
1513ff40c12SJohn Marino {
1523ff40c12SJohn Marino os_free(data->next_pseudonym);
153*a1157835SDaniel Fojt if (!(sm->eap_sim_id & 0x01)) {
154*a1157835SDaniel Fojt /* Use of pseudonyms disabled in configuration */
155*a1157835SDaniel Fojt data->next_pseudonym = NULL;
156*a1157835SDaniel Fojt } else if (!nonce_s) {
1573ff40c12SJohn Marino data->next_pseudonym =
1583ff40c12SJohn Marino eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
1593ff40c12SJohn Marino EAP_SIM_DB_SIM);
1603ff40c12SJohn Marino } else {
1613ff40c12SJohn Marino /* Do not update pseudonym during re-authentication */
1623ff40c12SJohn Marino data->next_pseudonym = NULL;
1633ff40c12SJohn Marino }
1643ff40c12SJohn Marino os_free(data->next_reauth_id);
165*a1157835SDaniel Fojt if (!(sm->eap_sim_id & 0x02)) {
166*a1157835SDaniel Fojt /* Use of fast reauth disabled in configuration */
167*a1157835SDaniel Fojt data->next_reauth_id = NULL;
168*a1157835SDaniel Fojt } else if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
1693ff40c12SJohn Marino data->next_reauth_id =
1703ff40c12SJohn Marino eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
1713ff40c12SJohn Marino EAP_SIM_DB_SIM);
1723ff40c12SJohn Marino } else {
1733ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
1743ff40c12SJohn Marino "count exceeded - force full authentication");
1753ff40c12SJohn Marino data->next_reauth_id = NULL;
1763ff40c12SJohn Marino }
1773ff40c12SJohn Marino
1783ff40c12SJohn Marino if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
1793ff40c12SJohn Marino counter == 0 && nonce_s == NULL)
1803ff40c12SJohn Marino return 0;
1813ff40c12SJohn Marino
1823ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_IV");
1833ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
1843ff40c12SJohn Marino eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
1853ff40c12SJohn Marino
1863ff40c12SJohn Marino if (counter > 0) {
1873ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
1883ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
1893ff40c12SJohn Marino }
1903ff40c12SJohn Marino
1913ff40c12SJohn Marino if (nonce_s) {
1923ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
1933ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
1943ff40c12SJohn Marino EAP_SIM_NONCE_S_LEN);
1953ff40c12SJohn Marino }
1963ff40c12SJohn Marino
1973ff40c12SJohn Marino if (data->next_pseudonym) {
1983ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
1993ff40c12SJohn Marino data->next_pseudonym);
2003ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
2013ff40c12SJohn Marino os_strlen(data->next_pseudonym),
2023ff40c12SJohn Marino (u8 *) data->next_pseudonym,
2033ff40c12SJohn Marino os_strlen(data->next_pseudonym));
2043ff40c12SJohn Marino }
2053ff40c12SJohn Marino
2063ff40c12SJohn Marino if (data->next_reauth_id) {
2073ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
2083ff40c12SJohn Marino data->next_reauth_id);
2093ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
2103ff40c12SJohn Marino os_strlen(data->next_reauth_id),
2113ff40c12SJohn Marino (u8 *) data->next_reauth_id,
2123ff40c12SJohn Marino os_strlen(data->next_reauth_id));
2133ff40c12SJohn Marino }
2143ff40c12SJohn Marino
2153ff40c12SJohn Marino if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
2163ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
2173ff40c12SJohn Marino "AT_ENCR_DATA");
2183ff40c12SJohn Marino return -1;
2193ff40c12SJohn Marino }
2203ff40c12SJohn Marino
2213ff40c12SJohn Marino return 0;
2223ff40c12SJohn Marino }
2233ff40c12SJohn Marino
2243ff40c12SJohn Marino
eap_sim_build_challenge(struct eap_sm * sm,struct eap_sim_data * data,u8 id)2253ff40c12SJohn Marino static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
2263ff40c12SJohn Marino struct eap_sim_data *data,
2273ff40c12SJohn Marino u8 id)
2283ff40c12SJohn Marino {
2293ff40c12SJohn Marino struct eap_sim_msg *msg;
2303ff40c12SJohn Marino
2313ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
2323ff40c12SJohn Marino msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
2333ff40c12SJohn Marino EAP_SIM_SUBTYPE_CHALLENGE);
2343ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_RAND");
2353ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
2363ff40c12SJohn Marino data->num_chal * GSM_RAND_LEN);
2373ff40c12SJohn Marino
2383ff40c12SJohn Marino if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
2393ff40c12SJohn Marino eap_sim_msg_free(msg);
2403ff40c12SJohn Marino return NULL;
2413ff40c12SJohn Marino }
2423ff40c12SJohn Marino
2433ff40c12SJohn Marino if (sm->eap_sim_aka_result_ind) {
2443ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
2453ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
2463ff40c12SJohn Marino }
2473ff40c12SJohn Marino
2483ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_MAC");
2493ff40c12SJohn Marino eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
250*a1157835SDaniel Fojt return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
251*a1157835SDaniel Fojt data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
2523ff40c12SJohn Marino }
2533ff40c12SJohn Marino
2543ff40c12SJohn Marino
eap_sim_build_reauth(struct eap_sm * sm,struct eap_sim_data * data,u8 id)2553ff40c12SJohn Marino static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
2563ff40c12SJohn Marino struct eap_sim_data *data, u8 id)
2573ff40c12SJohn Marino {
2583ff40c12SJohn Marino struct eap_sim_msg *msg;
259*a1157835SDaniel Fojt struct wpabuf *buf;
2603ff40c12SJohn Marino
2613ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
2623ff40c12SJohn Marino
2633ff40c12SJohn Marino if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
2643ff40c12SJohn Marino return NULL;
2653ff40c12SJohn Marino wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
2663ff40c12SJohn Marino data->nonce_s, EAP_SIM_NONCE_S_LEN);
2673ff40c12SJohn Marino
2683ff40c12SJohn Marino eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
2693ff40c12SJohn Marino data->emsk);
2703ff40c12SJohn Marino eap_sim_derive_keys_reauth(data->counter, sm->identity,
2713ff40c12SJohn Marino sm->identity_len, data->nonce_s, data->mk,
2723ff40c12SJohn Marino data->msk, data->emsk);
2733ff40c12SJohn Marino
2743ff40c12SJohn Marino msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
2753ff40c12SJohn Marino EAP_SIM_SUBTYPE_REAUTHENTICATION);
2763ff40c12SJohn Marino
2773ff40c12SJohn Marino if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
2783ff40c12SJohn Marino eap_sim_msg_free(msg);
2793ff40c12SJohn Marino return NULL;
2803ff40c12SJohn Marino }
2813ff40c12SJohn Marino
2823ff40c12SJohn Marino if (sm->eap_sim_aka_result_ind) {
2833ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
2843ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
2853ff40c12SJohn Marino }
2863ff40c12SJohn Marino
2873ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_MAC");
2883ff40c12SJohn Marino eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
289*a1157835SDaniel Fojt buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
290*a1157835SDaniel Fojt
291*a1157835SDaniel Fojt /* Remember this MAC before sending it to the peer. This MAC is used for
292*a1157835SDaniel Fojt * Session-Id calculation after receiving response from the peer and
293*a1157835SDaniel Fojt * after all other checks pass. */
294*a1157835SDaniel Fojt os_memcpy(data->reauth_mac,
295*a1157835SDaniel Fojt wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
296*a1157835SDaniel Fojt EAP_SIM_MAC_LEN);
297*a1157835SDaniel Fojt
298*a1157835SDaniel Fojt return buf;
2993ff40c12SJohn Marino }
3003ff40c12SJohn Marino
3013ff40c12SJohn Marino
eap_sim_build_notification(struct eap_sm * sm,struct eap_sim_data * data,u8 id)3023ff40c12SJohn Marino static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
3033ff40c12SJohn Marino struct eap_sim_data *data,
3043ff40c12SJohn Marino u8 id)
3053ff40c12SJohn Marino {
3063ff40c12SJohn Marino struct eap_sim_msg *msg;
3073ff40c12SJohn Marino
3083ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
3093ff40c12SJohn Marino msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
3103ff40c12SJohn Marino EAP_SIM_SUBTYPE_NOTIFICATION);
3113ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification);
3123ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
3133ff40c12SJohn Marino NULL, 0);
3143ff40c12SJohn Marino if (data->use_result_ind) {
3153ff40c12SJohn Marino if (data->reauth) {
3163ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_IV");
3173ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
3183ff40c12SJohn Marino eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
3193ff40c12SJohn Marino EAP_SIM_AT_ENCR_DATA);
3203ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)",
3213ff40c12SJohn Marino data->counter);
3223ff40c12SJohn Marino eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
3233ff40c12SJohn Marino NULL, 0);
3243ff40c12SJohn Marino
3253ff40c12SJohn Marino if (eap_sim_msg_add_encr_end(msg, data->k_encr,
3263ff40c12SJohn Marino EAP_SIM_AT_PADDING)) {
3273ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
3283ff40c12SJohn Marino "encrypt AT_ENCR_DATA");
3293ff40c12SJohn Marino eap_sim_msg_free(msg);
3303ff40c12SJohn Marino return NULL;
3313ff40c12SJohn Marino }
3323ff40c12SJohn Marino }
3333ff40c12SJohn Marino
3343ff40c12SJohn Marino wpa_printf(MSG_DEBUG, " AT_MAC");
3353ff40c12SJohn Marino eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
3363ff40c12SJohn Marino }
337*a1157835SDaniel Fojt return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
3383ff40c12SJohn Marino }
3393ff40c12SJohn Marino
3403ff40c12SJohn Marino
eap_sim_buildReq(struct eap_sm * sm,void * priv,u8 id)3413ff40c12SJohn Marino static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
3423ff40c12SJohn Marino {
3433ff40c12SJohn Marino struct eap_sim_data *data = priv;
3443ff40c12SJohn Marino
3453ff40c12SJohn Marino switch (data->state) {
3463ff40c12SJohn Marino case START:
3473ff40c12SJohn Marino return eap_sim_build_start(sm, data, id);
3483ff40c12SJohn Marino case CHALLENGE:
3493ff40c12SJohn Marino return eap_sim_build_challenge(sm, data, id);
3503ff40c12SJohn Marino case REAUTH:
3513ff40c12SJohn Marino return eap_sim_build_reauth(sm, data, id);
3523ff40c12SJohn Marino case NOTIFICATION:
3533ff40c12SJohn Marino return eap_sim_build_notification(sm, data, id);
3543ff40c12SJohn Marino default:
3553ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
3563ff40c12SJohn Marino "buildReq", data->state);
3573ff40c12SJohn Marino break;
3583ff40c12SJohn Marino }
3593ff40c12SJohn Marino return NULL;
3603ff40c12SJohn Marino }
3613ff40c12SJohn Marino
3623ff40c12SJohn Marino
eap_sim_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)3633ff40c12SJohn Marino static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
3643ff40c12SJohn Marino struct wpabuf *respData)
3653ff40c12SJohn Marino {
3663ff40c12SJohn Marino const u8 *pos;
3673ff40c12SJohn Marino size_t len;
3683ff40c12SJohn Marino
3693ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
3703ff40c12SJohn Marino if (pos == NULL || len < 3) {
3713ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
3723ff40c12SJohn Marino return TRUE;
3733ff40c12SJohn Marino }
3743ff40c12SJohn Marino
3753ff40c12SJohn Marino return FALSE;
3763ff40c12SJohn Marino }
3773ff40c12SJohn Marino
3783ff40c12SJohn Marino
eap_sim_unexpected_subtype(struct eap_sim_data * data,u8 subtype)3793ff40c12SJohn Marino static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data,
3803ff40c12SJohn Marino u8 subtype)
3813ff40c12SJohn Marino {
3823ff40c12SJohn Marino if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
3833ff40c12SJohn Marino return FALSE;
3843ff40c12SJohn Marino
3853ff40c12SJohn Marino switch (data->state) {
3863ff40c12SJohn Marino case START:
3873ff40c12SJohn Marino if (subtype != EAP_SIM_SUBTYPE_START) {
3883ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
3893ff40c12SJohn Marino "subtype %d", subtype);
3903ff40c12SJohn Marino return TRUE;
3913ff40c12SJohn Marino }
3923ff40c12SJohn Marino break;
3933ff40c12SJohn Marino case CHALLENGE:
3943ff40c12SJohn Marino if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
3953ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
3963ff40c12SJohn Marino "subtype %d", subtype);
3973ff40c12SJohn Marino return TRUE;
3983ff40c12SJohn Marino }
3993ff40c12SJohn Marino break;
4003ff40c12SJohn Marino case REAUTH:
4013ff40c12SJohn Marino if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
4023ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
4033ff40c12SJohn Marino "subtype %d", subtype);
4043ff40c12SJohn Marino return TRUE;
4053ff40c12SJohn Marino }
4063ff40c12SJohn Marino break;
4073ff40c12SJohn Marino case NOTIFICATION:
4083ff40c12SJohn Marino if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
4093ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
4103ff40c12SJohn Marino "subtype %d", subtype);
4113ff40c12SJohn Marino return TRUE;
4123ff40c12SJohn Marino }
4133ff40c12SJohn Marino break;
4143ff40c12SJohn Marino default:
4153ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
4163ff40c12SJohn Marino "processing a response", data->state);
4173ff40c12SJohn Marino return TRUE;
4183ff40c12SJohn Marino }
4193ff40c12SJohn Marino
4203ff40c12SJohn Marino return FALSE;
4213ff40c12SJohn Marino }
4223ff40c12SJohn Marino
4233ff40c12SJohn Marino
eap_sim_supported_ver(struct eap_sim_data * data,int version)4243ff40c12SJohn Marino static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
4253ff40c12SJohn Marino {
4263ff40c12SJohn Marino return version == EAP_SIM_VERSION;
4273ff40c12SJohn Marino }
4283ff40c12SJohn Marino
4293ff40c12SJohn Marino
eap_sim_process_start(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)4303ff40c12SJohn Marino static void eap_sim_process_start(struct eap_sm *sm,
4313ff40c12SJohn Marino struct eap_sim_data *data,
4323ff40c12SJohn Marino struct wpabuf *respData,
4333ff40c12SJohn Marino struct eap_sim_attrs *attr)
4343ff40c12SJohn Marino {
4353ff40c12SJohn Marino size_t identity_len;
4363ff40c12SJohn Marino u8 ver_list[2];
4373ff40c12SJohn Marino u8 *new_identity;
4383ff40c12SJohn Marino char *username;
4393ff40c12SJohn Marino
4403ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
4413ff40c12SJohn Marino
4423ff40c12SJohn Marino if (data->start_round == 0) {
4433ff40c12SJohn Marino /*
4443ff40c12SJohn Marino * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
4453ff40c12SJohn Marino * was requested since we already know it.
4463ff40c12SJohn Marino */
4473ff40c12SJohn Marino goto skip_id_update;
4483ff40c12SJohn Marino }
4493ff40c12SJohn Marino
4503ff40c12SJohn Marino /*
4513ff40c12SJohn Marino * We always request identity in SIM/Start, so the peer is required to
4523ff40c12SJohn Marino * have replied with one.
4533ff40c12SJohn Marino */
4543ff40c12SJohn Marino if (!attr->identity || attr->identity_len == 0) {
4553ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
4563ff40c12SJohn Marino "identity");
4573ff40c12SJohn Marino goto failed;
4583ff40c12SJohn Marino }
4593ff40c12SJohn Marino
4603ff40c12SJohn Marino new_identity = os_malloc(attr->identity_len);
4613ff40c12SJohn Marino if (new_identity == NULL)
4623ff40c12SJohn Marino goto failed;
4633ff40c12SJohn Marino os_free(sm->identity);
4643ff40c12SJohn Marino sm->identity = new_identity;
4653ff40c12SJohn Marino os_memcpy(sm->identity, attr->identity, attr->identity_len);
4663ff40c12SJohn Marino sm->identity_len = attr->identity_len;
4673ff40c12SJohn Marino
4683ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
4693ff40c12SJohn Marino sm->identity, sm->identity_len);
4703ff40c12SJohn Marino username = sim_get_username(sm->identity, sm->identity_len);
4713ff40c12SJohn Marino if (username == NULL)
4723ff40c12SJohn Marino goto failed;
4733ff40c12SJohn Marino
4743ff40c12SJohn Marino if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
4753ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
4763ff40c12SJohn Marino username);
4773ff40c12SJohn Marino data->reauth = eap_sim_db_get_reauth_entry(
4783ff40c12SJohn Marino sm->eap_sim_db_priv, username);
4793ff40c12SJohn Marino os_free(username);
4803ff40c12SJohn Marino if (data->reauth == NULL) {
4813ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
4823ff40c12SJohn Marino "identity - request full auth identity");
4833ff40c12SJohn Marino /* Remain in START state for another round */
4843ff40c12SJohn Marino return;
4853ff40c12SJohn Marino }
4863ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
4873ff40c12SJohn Marino os_strlcpy(data->permanent, data->reauth->permanent,
4883ff40c12SJohn Marino sizeof(data->permanent));
4893ff40c12SJohn Marino data->counter = data->reauth->counter;
4903ff40c12SJohn Marino os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
4913ff40c12SJohn Marino eap_sim_state(data, REAUTH);
4923ff40c12SJohn Marino return;
4933ff40c12SJohn Marino }
4943ff40c12SJohn Marino
4953ff40c12SJohn Marino if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
4963ff40c12SJohn Marino const char *permanent;
4973ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
4983ff40c12SJohn Marino username);
4993ff40c12SJohn Marino permanent = eap_sim_db_get_permanent(
5003ff40c12SJohn Marino sm->eap_sim_db_priv, username);
5013ff40c12SJohn Marino os_free(username);
5023ff40c12SJohn Marino if (permanent == NULL) {
5033ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
5043ff40c12SJohn Marino "identity - request permanent identity");
5053ff40c12SJohn Marino /* Remain in START state for another round */
5063ff40c12SJohn Marino return;
5073ff40c12SJohn Marino }
5083ff40c12SJohn Marino os_strlcpy(data->permanent, permanent,
5093ff40c12SJohn Marino sizeof(data->permanent));
5103ff40c12SJohn Marino } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
5113ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
5123ff40c12SJohn Marino username);
5133ff40c12SJohn Marino os_strlcpy(data->permanent, username, sizeof(data->permanent));
5143ff40c12SJohn Marino os_free(username);
5153ff40c12SJohn Marino } else {
5163ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
5173ff40c12SJohn Marino username);
5183ff40c12SJohn Marino os_free(username);
5193ff40c12SJohn Marino goto failed;
5203ff40c12SJohn Marino }
5213ff40c12SJohn Marino
5223ff40c12SJohn Marino skip_id_update:
5233ff40c12SJohn Marino /* Full authentication */
5243ff40c12SJohn Marino
5253ff40c12SJohn Marino if (attr->nonce_mt == NULL || attr->selected_version < 0) {
5263ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
5273ff40c12SJohn Marino "required attributes");
5283ff40c12SJohn Marino goto failed;
5293ff40c12SJohn Marino }
5303ff40c12SJohn Marino
5313ff40c12SJohn Marino if (!eap_sim_supported_ver(data, attr->selected_version)) {
5323ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
5333ff40c12SJohn Marino "version %d", attr->selected_version);
5343ff40c12SJohn Marino goto failed;
5353ff40c12SJohn Marino }
5363ff40c12SJohn Marino
5373ff40c12SJohn Marino data->counter = 0; /* reset re-auth counter since this is full auth */
5383ff40c12SJohn Marino data->reauth = NULL;
5393ff40c12SJohn Marino
5403ff40c12SJohn Marino data->num_chal = eap_sim_db_get_gsm_triplets(
5413ff40c12SJohn Marino sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
5423ff40c12SJohn Marino (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
5433ff40c12SJohn Marino if (data->num_chal == EAP_SIM_DB_PENDING) {
5443ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
5453ff40c12SJohn Marino "not yet available - pending request");
5463ff40c12SJohn Marino sm->method_pending = METHOD_PENDING_WAIT;
5473ff40c12SJohn Marino return;
5483ff40c12SJohn Marino }
5493ff40c12SJohn Marino if (data->num_chal < 2) {
5503ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
5513ff40c12SJohn Marino "authentication triplets for the peer");
5523ff40c12SJohn Marino goto failed;
5533ff40c12SJohn Marino }
5543ff40c12SJohn Marino
555*a1157835SDaniel Fojt if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX)
556*a1157835SDaniel Fojt os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi));
557*a1157835SDaniel Fojt
5583ff40c12SJohn Marino identity_len = sm->identity_len;
5593ff40c12SJohn Marino while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
5603ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
5613ff40c12SJohn Marino "character from identity");
5623ff40c12SJohn Marino identity_len--;
5633ff40c12SJohn Marino }
5643ff40c12SJohn Marino wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
5653ff40c12SJohn Marino sm->identity, identity_len);
5663ff40c12SJohn Marino
5673ff40c12SJohn Marino os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
5683ff40c12SJohn Marino WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
5693ff40c12SJohn Marino eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
5703ff40c12SJohn Marino attr->selected_version, ver_list, sizeof(ver_list),
5713ff40c12SJohn Marino data->num_chal, (const u8 *) data->kc, data->mk);
5723ff40c12SJohn Marino eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
5733ff40c12SJohn Marino data->emsk);
5743ff40c12SJohn Marino
5753ff40c12SJohn Marino eap_sim_state(data, CHALLENGE);
5763ff40c12SJohn Marino return;
5773ff40c12SJohn Marino
5783ff40c12SJohn Marino failed:
5793ff40c12SJohn Marino data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
5803ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
5813ff40c12SJohn Marino }
5823ff40c12SJohn Marino
5833ff40c12SJohn Marino
eap_sim_process_challenge(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)5843ff40c12SJohn Marino static void eap_sim_process_challenge(struct eap_sm *sm,
5853ff40c12SJohn Marino struct eap_sim_data *data,
5863ff40c12SJohn Marino struct wpabuf *respData,
5873ff40c12SJohn Marino struct eap_sim_attrs *attr)
5883ff40c12SJohn Marino {
5893ff40c12SJohn Marino if (attr->mac == NULL ||
5903ff40c12SJohn Marino eap_sim_verify_mac(data->k_aut, respData, attr->mac,
5913ff40c12SJohn Marino (u8 *) data->sres,
5923ff40c12SJohn Marino data->num_chal * EAP_SIM_SRES_LEN)) {
5933ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
5943ff40c12SJohn Marino "did not include valid AT_MAC");
5953ff40c12SJohn Marino data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
5963ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
5973ff40c12SJohn Marino return;
5983ff40c12SJohn Marino }
5993ff40c12SJohn Marino
6003ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
6013ff40c12SJohn Marino "correct AT_MAC");
6023ff40c12SJohn Marino if (sm->eap_sim_aka_result_ind && attr->result_ind) {
6033ff40c12SJohn Marino data->use_result_ind = 1;
6043ff40c12SJohn Marino data->notification = EAP_SIM_SUCCESS;
6053ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
6063ff40c12SJohn Marino } else
6073ff40c12SJohn Marino eap_sim_state(data, SUCCESS);
6083ff40c12SJohn Marino
6093ff40c12SJohn Marino if (data->next_pseudonym) {
6103ff40c12SJohn Marino eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
6113ff40c12SJohn Marino data->next_pseudonym);
6123ff40c12SJohn Marino data->next_pseudonym = NULL;
6133ff40c12SJohn Marino }
6143ff40c12SJohn Marino if (data->next_reauth_id) {
6153ff40c12SJohn Marino eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
6163ff40c12SJohn Marino data->next_reauth_id, data->counter + 1,
6173ff40c12SJohn Marino data->mk);
6183ff40c12SJohn Marino data->next_reauth_id = NULL;
6193ff40c12SJohn Marino }
6203ff40c12SJohn Marino }
6213ff40c12SJohn Marino
6223ff40c12SJohn Marino
eap_sim_process_reauth(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)6233ff40c12SJohn Marino static void eap_sim_process_reauth(struct eap_sm *sm,
6243ff40c12SJohn Marino struct eap_sim_data *data,
6253ff40c12SJohn Marino struct wpabuf *respData,
6263ff40c12SJohn Marino struct eap_sim_attrs *attr)
6273ff40c12SJohn Marino {
6283ff40c12SJohn Marino struct eap_sim_attrs eattr;
6293ff40c12SJohn Marino u8 *decrypted = NULL;
6303ff40c12SJohn Marino
6313ff40c12SJohn Marino if (attr->mac == NULL ||
6323ff40c12SJohn Marino eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
6333ff40c12SJohn Marino EAP_SIM_NONCE_S_LEN)) {
6343ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
6353ff40c12SJohn Marino "did not include valid AT_MAC");
6363ff40c12SJohn Marino goto fail;
6373ff40c12SJohn Marino }
6383ff40c12SJohn Marino
6393ff40c12SJohn Marino if (attr->encr_data == NULL || attr->iv == NULL) {
6403ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
6413ff40c12SJohn Marino "message did not include encrypted data");
6423ff40c12SJohn Marino goto fail;
6433ff40c12SJohn Marino }
6443ff40c12SJohn Marino
6453ff40c12SJohn Marino decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
6463ff40c12SJohn Marino attr->encr_data_len, attr->iv, &eattr,
6473ff40c12SJohn Marino 0);
6483ff40c12SJohn Marino if (decrypted == NULL) {
6493ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
6503ff40c12SJohn Marino "data from reauthentication message");
6513ff40c12SJohn Marino goto fail;
6523ff40c12SJohn Marino }
6533ff40c12SJohn Marino
6543ff40c12SJohn Marino if (eattr.counter != data->counter) {
6553ff40c12SJohn Marino wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
6563ff40c12SJohn Marino "used incorrect counter %u, expected %u",
6573ff40c12SJohn Marino eattr.counter, data->counter);
6583ff40c12SJohn Marino goto fail;
6593ff40c12SJohn Marino }
6603ff40c12SJohn Marino os_free(decrypted);
6613ff40c12SJohn Marino decrypted = NULL;
6623ff40c12SJohn Marino
6633ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
6643ff40c12SJohn Marino "the correct AT_MAC");
6653ff40c12SJohn Marino
6663ff40c12SJohn Marino if (eattr.counter_too_small) {
6673ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
6683ff40c12SJohn Marino "included AT_COUNTER_TOO_SMALL - starting full "
6693ff40c12SJohn Marino "authentication");
6703ff40c12SJohn Marino data->start_round = -1;
6713ff40c12SJohn Marino eap_sim_state(data, START);
6723ff40c12SJohn Marino return;
6733ff40c12SJohn Marino }
6743ff40c12SJohn Marino
6753ff40c12SJohn Marino if (sm->eap_sim_aka_result_ind && attr->result_ind) {
6763ff40c12SJohn Marino data->use_result_ind = 1;
6773ff40c12SJohn Marino data->notification = EAP_SIM_SUCCESS;
6783ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
6793ff40c12SJohn Marino } else
6803ff40c12SJohn Marino eap_sim_state(data, SUCCESS);
6813ff40c12SJohn Marino
6823ff40c12SJohn Marino if (data->next_reauth_id) {
6833ff40c12SJohn Marino eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
6843ff40c12SJohn Marino data->next_reauth_id,
6853ff40c12SJohn Marino data->counter + 1, data->mk);
6863ff40c12SJohn Marino data->next_reauth_id = NULL;
6873ff40c12SJohn Marino } else {
6883ff40c12SJohn Marino eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
6893ff40c12SJohn Marino data->reauth = NULL;
6903ff40c12SJohn Marino }
6913ff40c12SJohn Marino
6923ff40c12SJohn Marino return;
6933ff40c12SJohn Marino
6943ff40c12SJohn Marino fail:
6953ff40c12SJohn Marino data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
6963ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
6973ff40c12SJohn Marino eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
6983ff40c12SJohn Marino data->reauth = NULL;
6993ff40c12SJohn Marino os_free(decrypted);
7003ff40c12SJohn Marino }
7013ff40c12SJohn Marino
7023ff40c12SJohn Marino
eap_sim_process_client_error(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)7033ff40c12SJohn Marino static void eap_sim_process_client_error(struct eap_sm *sm,
7043ff40c12SJohn Marino struct eap_sim_data *data,
7053ff40c12SJohn Marino struct wpabuf *respData,
7063ff40c12SJohn Marino struct eap_sim_attrs *attr)
7073ff40c12SJohn Marino {
7083ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
7093ff40c12SJohn Marino attr->client_error_code);
7103ff40c12SJohn Marino if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
7113ff40c12SJohn Marino eap_sim_state(data, SUCCESS);
7123ff40c12SJohn Marino else
7133ff40c12SJohn Marino eap_sim_state(data, FAILURE);
7143ff40c12SJohn Marino }
7153ff40c12SJohn Marino
7163ff40c12SJohn Marino
eap_sim_process_notification(struct eap_sm * sm,struct eap_sim_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)7173ff40c12SJohn Marino static void eap_sim_process_notification(struct eap_sm *sm,
7183ff40c12SJohn Marino struct eap_sim_data *data,
7193ff40c12SJohn Marino struct wpabuf *respData,
7203ff40c12SJohn Marino struct eap_sim_attrs *attr)
7213ff40c12SJohn Marino {
7223ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
7233ff40c12SJohn Marino if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
7243ff40c12SJohn Marino eap_sim_state(data, SUCCESS);
7253ff40c12SJohn Marino else
7263ff40c12SJohn Marino eap_sim_state(data, FAILURE);
7273ff40c12SJohn Marino }
7283ff40c12SJohn Marino
7293ff40c12SJohn Marino
eap_sim_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)7303ff40c12SJohn Marino static void eap_sim_process(struct eap_sm *sm, void *priv,
7313ff40c12SJohn Marino struct wpabuf *respData)
7323ff40c12SJohn Marino {
7333ff40c12SJohn Marino struct eap_sim_data *data = priv;
7343ff40c12SJohn Marino const u8 *pos, *end;
7353ff40c12SJohn Marino u8 subtype;
7363ff40c12SJohn Marino size_t len;
7373ff40c12SJohn Marino struct eap_sim_attrs attr;
7383ff40c12SJohn Marino
7393ff40c12SJohn Marino pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
7403ff40c12SJohn Marino if (pos == NULL || len < 3)
7413ff40c12SJohn Marino return;
7423ff40c12SJohn Marino
7433ff40c12SJohn Marino end = pos + len;
7443ff40c12SJohn Marino subtype = *pos;
7453ff40c12SJohn Marino pos += 3;
7463ff40c12SJohn Marino
7473ff40c12SJohn Marino if (eap_sim_unexpected_subtype(data, subtype)) {
7483ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
7493ff40c12SJohn Marino "EAP-SIM Subtype in EAP Response");
7503ff40c12SJohn Marino data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
7513ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
7523ff40c12SJohn Marino return;
7533ff40c12SJohn Marino }
7543ff40c12SJohn Marino
7553ff40c12SJohn Marino if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
7563ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
7573ff40c12SJohn Marino if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
7583ff40c12SJohn Marino (data->state == START || data->state == CHALLENGE ||
7593ff40c12SJohn Marino data->state == REAUTH)) {
7603ff40c12SJohn Marino data->notification =
7613ff40c12SJohn Marino EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
7623ff40c12SJohn Marino eap_sim_state(data, NOTIFICATION);
7633ff40c12SJohn Marino return;
7643ff40c12SJohn Marino }
7653ff40c12SJohn Marino eap_sim_state(data, FAILURE);
7663ff40c12SJohn Marino return;
7673ff40c12SJohn Marino }
7683ff40c12SJohn Marino
7693ff40c12SJohn Marino if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
7703ff40c12SJohn Marino eap_sim_process_client_error(sm, data, respData, &attr);
7713ff40c12SJohn Marino return;
7723ff40c12SJohn Marino }
7733ff40c12SJohn Marino
7743ff40c12SJohn Marino switch (data->state) {
7753ff40c12SJohn Marino case START:
7763ff40c12SJohn Marino eap_sim_process_start(sm, data, respData, &attr);
7773ff40c12SJohn Marino break;
7783ff40c12SJohn Marino case CHALLENGE:
7793ff40c12SJohn Marino eap_sim_process_challenge(sm, data, respData, &attr);
7803ff40c12SJohn Marino break;
7813ff40c12SJohn Marino case REAUTH:
7823ff40c12SJohn Marino eap_sim_process_reauth(sm, data, respData, &attr);
7833ff40c12SJohn Marino break;
7843ff40c12SJohn Marino case NOTIFICATION:
7853ff40c12SJohn Marino eap_sim_process_notification(sm, data, respData, &attr);
7863ff40c12SJohn Marino break;
7873ff40c12SJohn Marino default:
7883ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
7893ff40c12SJohn Marino "process", data->state);
7903ff40c12SJohn Marino break;
7913ff40c12SJohn Marino }
7923ff40c12SJohn Marino }
7933ff40c12SJohn Marino
7943ff40c12SJohn Marino
eap_sim_isDone(struct eap_sm * sm,void * priv)7953ff40c12SJohn Marino static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
7963ff40c12SJohn Marino {
7973ff40c12SJohn Marino struct eap_sim_data *data = priv;
7983ff40c12SJohn Marino return data->state == SUCCESS || data->state == FAILURE;
7993ff40c12SJohn Marino }
8003ff40c12SJohn Marino
8013ff40c12SJohn Marino
eap_sim_getKey(struct eap_sm * sm,void * priv,size_t * len)8023ff40c12SJohn Marino static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
8033ff40c12SJohn Marino {
8043ff40c12SJohn Marino struct eap_sim_data *data = priv;
8053ff40c12SJohn Marino u8 *key;
8063ff40c12SJohn Marino
8073ff40c12SJohn Marino if (data->state != SUCCESS)
8083ff40c12SJohn Marino return NULL;
8093ff40c12SJohn Marino
810*a1157835SDaniel Fojt key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
8113ff40c12SJohn Marino if (key == NULL)
8123ff40c12SJohn Marino return NULL;
8133ff40c12SJohn Marino *len = EAP_SIM_KEYING_DATA_LEN;
8143ff40c12SJohn Marino return key;
8153ff40c12SJohn Marino }
8163ff40c12SJohn Marino
8173ff40c12SJohn Marino
eap_sim_get_emsk(struct eap_sm * sm,void * priv,size_t * len)8183ff40c12SJohn Marino static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
8193ff40c12SJohn Marino {
8203ff40c12SJohn Marino struct eap_sim_data *data = priv;
8213ff40c12SJohn Marino u8 *key;
8223ff40c12SJohn Marino
8233ff40c12SJohn Marino if (data->state != SUCCESS)
8243ff40c12SJohn Marino return NULL;
8253ff40c12SJohn Marino
826*a1157835SDaniel Fojt key = os_memdup(data->emsk, EAP_EMSK_LEN);
8273ff40c12SJohn Marino if (key == NULL)
8283ff40c12SJohn Marino return NULL;
8293ff40c12SJohn Marino *len = EAP_EMSK_LEN;
8303ff40c12SJohn Marino return key;
8313ff40c12SJohn Marino }
8323ff40c12SJohn Marino
8333ff40c12SJohn Marino
eap_sim_isSuccess(struct eap_sm * sm,void * priv)8343ff40c12SJohn Marino static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
8353ff40c12SJohn Marino {
8363ff40c12SJohn Marino struct eap_sim_data *data = priv;
8373ff40c12SJohn Marino return data->state == SUCCESS;
8383ff40c12SJohn Marino }
8393ff40c12SJohn Marino
8403ff40c12SJohn Marino
eap_sim_get_session_id(struct eap_sm * sm,void * priv,size_t * len)841*a1157835SDaniel Fojt static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
842*a1157835SDaniel Fojt {
843*a1157835SDaniel Fojt struct eap_sim_data *data = priv;
844*a1157835SDaniel Fojt u8 *id;
845*a1157835SDaniel Fojt
846*a1157835SDaniel Fojt if (data->state != SUCCESS)
847*a1157835SDaniel Fojt return NULL;
848*a1157835SDaniel Fojt
849*a1157835SDaniel Fojt if (!data->reauth)
850*a1157835SDaniel Fojt *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
851*a1157835SDaniel Fojt else
852*a1157835SDaniel Fojt *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
853*a1157835SDaniel Fojt id = os_malloc(*len);
854*a1157835SDaniel Fojt if (id == NULL)
855*a1157835SDaniel Fojt return NULL;
856*a1157835SDaniel Fojt
857*a1157835SDaniel Fojt id[0] = EAP_TYPE_SIM;
858*a1157835SDaniel Fojt if (!data->reauth) {
859*a1157835SDaniel Fojt os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
860*a1157835SDaniel Fojt os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN,
861*a1157835SDaniel Fojt data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
862*a1157835SDaniel Fojt } else {
863*a1157835SDaniel Fojt os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
864*a1157835SDaniel Fojt os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
865*a1157835SDaniel Fojt EAP_SIM_MAC_LEN);
866*a1157835SDaniel Fojt
867*a1157835SDaniel Fojt }
868*a1157835SDaniel Fojt wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
869*a1157835SDaniel Fojt
870*a1157835SDaniel Fojt return id;
871*a1157835SDaniel Fojt }
872*a1157835SDaniel Fojt
873*a1157835SDaniel Fojt
eap_server_sim_register(void)8743ff40c12SJohn Marino int eap_server_sim_register(void)
8753ff40c12SJohn Marino {
8763ff40c12SJohn Marino struct eap_method *eap;
8773ff40c12SJohn Marino
8783ff40c12SJohn Marino eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
8793ff40c12SJohn Marino EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
8803ff40c12SJohn Marino if (eap == NULL)
8813ff40c12SJohn Marino return -1;
8823ff40c12SJohn Marino
8833ff40c12SJohn Marino eap->init = eap_sim_init;
8843ff40c12SJohn Marino eap->reset = eap_sim_reset;
8853ff40c12SJohn Marino eap->buildReq = eap_sim_buildReq;
8863ff40c12SJohn Marino eap->check = eap_sim_check;
8873ff40c12SJohn Marino eap->process = eap_sim_process;
8883ff40c12SJohn Marino eap->isDone = eap_sim_isDone;
8893ff40c12SJohn Marino eap->getKey = eap_sim_getKey;
8903ff40c12SJohn Marino eap->isSuccess = eap_sim_isSuccess;
8913ff40c12SJohn Marino eap->get_emsk = eap_sim_get_emsk;
892*a1157835SDaniel Fojt eap->getSessionId = eap_sim_get_session_id;
8933ff40c12SJohn Marino
894*a1157835SDaniel Fojt return eap_server_method_register(eap);
8953ff40c12SJohn Marino }
896