xref: /dflybsd-src/contrib/wpa_supplicant/src/eap_server/eap_sim_db.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * hostapd / EAP-SIM database/authenticator gateway
33ff40c12SJohn Marino  * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  *
86d49e1aeSJan Lentfer  * This is an example implementation of the EAP-SIM/AKA database/authentication
96d49e1aeSJan Lentfer  * gateway interface that is using an external program as an SS7 gateway to
106d49e1aeSJan Lentfer  * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
116d49e1aeSJan Lentfer  * implementation of such a gateway program. This eap_sim_db.c takes care of
126d49e1aeSJan Lentfer  * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
136d49e1aeSJan Lentfer  * gateway implementations for HLR/AuC access. Alternatively, it can also be
146d49e1aeSJan Lentfer  * completely replaced if the in-memory database of pseudonyms/re-auth
156d49e1aeSJan Lentfer  * identities is not suitable for some cases.
166d49e1aeSJan Lentfer  */
176d49e1aeSJan Lentfer 
186d49e1aeSJan Lentfer #include "includes.h"
196d49e1aeSJan Lentfer #include <sys/un.h>
203ff40c12SJohn Marino #ifdef CONFIG_SQLITE
213ff40c12SJohn Marino #include <sqlite3.h>
223ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
236d49e1aeSJan Lentfer 
246d49e1aeSJan Lentfer #include "common.h"
253ff40c12SJohn Marino #include "crypto/random.h"
266d49e1aeSJan Lentfer #include "eap_common/eap_sim_common.h"
276d49e1aeSJan Lentfer #include "eap_server/eap_sim_db.h"
286d49e1aeSJan Lentfer #include "eloop.h"
296d49e1aeSJan Lentfer 
306d49e1aeSJan Lentfer struct eap_sim_pseudonym {
316d49e1aeSJan Lentfer 	struct eap_sim_pseudonym *next;
323ff40c12SJohn Marino 	char *permanent; /* permanent username */
333ff40c12SJohn Marino 	char *pseudonym; /* pseudonym username */
346d49e1aeSJan Lentfer };
356d49e1aeSJan Lentfer 
366d49e1aeSJan Lentfer struct eap_sim_db_pending {
376d49e1aeSJan Lentfer 	struct eap_sim_db_pending *next;
383ff40c12SJohn Marino 	char imsi[20];
396d49e1aeSJan Lentfer 	enum { PENDING, SUCCESS, FAILURE } state;
406d49e1aeSJan Lentfer 	void *cb_session_ctx;
416d49e1aeSJan Lentfer 	int aka;
426d49e1aeSJan Lentfer 	union {
436d49e1aeSJan Lentfer 		struct {
446d49e1aeSJan Lentfer 			u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
456d49e1aeSJan Lentfer 			u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
466d49e1aeSJan Lentfer 			u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
476d49e1aeSJan Lentfer 			int num_chal;
486d49e1aeSJan Lentfer 		} sim;
496d49e1aeSJan Lentfer 		struct {
506d49e1aeSJan Lentfer 			u8 rand[EAP_AKA_RAND_LEN];
516d49e1aeSJan Lentfer 			u8 autn[EAP_AKA_AUTN_LEN];
526d49e1aeSJan Lentfer 			u8 ik[EAP_AKA_IK_LEN];
536d49e1aeSJan Lentfer 			u8 ck[EAP_AKA_CK_LEN];
546d49e1aeSJan Lentfer 			u8 res[EAP_AKA_RES_MAX_LEN];
556d49e1aeSJan Lentfer 			size_t res_len;
566d49e1aeSJan Lentfer 		} aka;
576d49e1aeSJan Lentfer 	} u;
586d49e1aeSJan Lentfer };
596d49e1aeSJan Lentfer 
606d49e1aeSJan Lentfer struct eap_sim_db_data {
616d49e1aeSJan Lentfer 	int sock;
626d49e1aeSJan Lentfer 	char *fname;
636d49e1aeSJan Lentfer 	char *local_sock;
646d49e1aeSJan Lentfer 	void (*get_complete_cb)(void *ctx, void *session_ctx);
656d49e1aeSJan Lentfer 	void *ctx;
666d49e1aeSJan Lentfer 	struct eap_sim_pseudonym *pseudonyms;
676d49e1aeSJan Lentfer 	struct eap_sim_reauth *reauths;
686d49e1aeSJan Lentfer 	struct eap_sim_db_pending *pending;
69*a1157835SDaniel Fojt 	unsigned int eap_sim_db_timeout;
703ff40c12SJohn Marino #ifdef CONFIG_SQLITE
713ff40c12SJohn Marino 	sqlite3 *sqlite_db;
723ff40c12SJohn Marino 	char db_tmp_identity[100];
733ff40c12SJohn Marino 	char db_tmp_pseudonym_str[100];
743ff40c12SJohn Marino 	struct eap_sim_pseudonym db_tmp_pseudonym;
753ff40c12SJohn Marino 	struct eap_sim_reauth db_tmp_reauth;
763ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
776d49e1aeSJan Lentfer };
786d49e1aeSJan Lentfer 
796d49e1aeSJan Lentfer 
80*a1157835SDaniel Fojt static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx);
81*a1157835SDaniel Fojt static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx);
82*a1157835SDaniel Fojt 
83*a1157835SDaniel Fojt 
843ff40c12SJohn Marino #ifdef CONFIG_SQLITE
853ff40c12SJohn Marino 
db_table_exists(sqlite3 * db,const char * name)863ff40c12SJohn Marino static int db_table_exists(sqlite3 *db, const char *name)
873ff40c12SJohn Marino {
883ff40c12SJohn Marino 	char cmd[128];
893ff40c12SJohn Marino 	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
903ff40c12SJohn Marino 	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
913ff40c12SJohn Marino }
923ff40c12SJohn Marino 
933ff40c12SJohn Marino 
db_table_create_pseudonym(sqlite3 * db)943ff40c12SJohn Marino static int db_table_create_pseudonym(sqlite3 *db)
953ff40c12SJohn Marino {
963ff40c12SJohn Marino 	char *err = NULL;
973ff40c12SJohn Marino 	const char *sql =
983ff40c12SJohn Marino 		"CREATE TABLE pseudonyms("
993ff40c12SJohn Marino 		"  permanent CHAR(21) PRIMARY KEY,"
1003ff40c12SJohn Marino 		"  pseudonym CHAR(21) NOT NULL"
1013ff40c12SJohn Marino 		");";
1023ff40c12SJohn Marino 
1033ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
1043ff40c12SJohn Marino 		   "pseudonym information");
1053ff40c12SJohn Marino 	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
1063ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
1073ff40c12SJohn Marino 		sqlite3_free(err);
1083ff40c12SJohn Marino 		return -1;
1093ff40c12SJohn Marino 	}
1103ff40c12SJohn Marino 
1113ff40c12SJohn Marino 	return 0;
1123ff40c12SJohn Marino }
1133ff40c12SJohn Marino 
1143ff40c12SJohn Marino 
db_table_create_reauth(sqlite3 * db)1153ff40c12SJohn Marino static int db_table_create_reauth(sqlite3 *db)
1163ff40c12SJohn Marino {
1173ff40c12SJohn Marino 	char *err = NULL;
1183ff40c12SJohn Marino 	const char *sql =
1193ff40c12SJohn Marino 		"CREATE TABLE reauth("
1203ff40c12SJohn Marino 		"  permanent CHAR(21) PRIMARY KEY,"
1213ff40c12SJohn Marino 		"  reauth_id CHAR(21) NOT NULL,"
1223ff40c12SJohn Marino 		"  counter INTEGER,"
1233ff40c12SJohn Marino 		"  mk CHAR(40),"
1243ff40c12SJohn Marino 		"  k_encr CHAR(32),"
1253ff40c12SJohn Marino 		"  k_aut CHAR(64),"
1263ff40c12SJohn Marino 		"  k_re CHAR(64)"
1273ff40c12SJohn Marino 		");";
1283ff40c12SJohn Marino 
1293ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
1303ff40c12SJohn Marino 		   "reauth information");
1313ff40c12SJohn Marino 	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
1323ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
1333ff40c12SJohn Marino 		sqlite3_free(err);
1343ff40c12SJohn Marino 		return -1;
1353ff40c12SJohn Marino 	}
1363ff40c12SJohn Marino 
1373ff40c12SJohn Marino 	return 0;
1383ff40c12SJohn Marino }
1393ff40c12SJohn Marino 
1403ff40c12SJohn Marino 
db_open(const char * db_file)1413ff40c12SJohn Marino static sqlite3 * db_open(const char *db_file)
1423ff40c12SJohn Marino {
1433ff40c12SJohn Marino 	sqlite3 *db;
1443ff40c12SJohn Marino 
1453ff40c12SJohn Marino 	if (sqlite3_open(db_file, &db)) {
1463ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
1473ff40c12SJohn Marino 			   "%s: %s", db_file, sqlite3_errmsg(db));
1483ff40c12SJohn Marino 		sqlite3_close(db);
1493ff40c12SJohn Marino 		return NULL;
1503ff40c12SJohn Marino 	}
1513ff40c12SJohn Marino 
1523ff40c12SJohn Marino 	if (!db_table_exists(db, "pseudonyms") &&
1533ff40c12SJohn Marino 	    db_table_create_pseudonym(db) < 0) {
1543ff40c12SJohn Marino 		sqlite3_close(db);
1553ff40c12SJohn Marino 		return NULL;
1563ff40c12SJohn Marino 	}
1573ff40c12SJohn Marino 
1583ff40c12SJohn Marino 	if (!db_table_exists(db, "reauth") &&
1593ff40c12SJohn Marino 	    db_table_create_reauth(db) < 0) {
1603ff40c12SJohn Marino 		sqlite3_close(db);
1613ff40c12SJohn Marino 		return NULL;
1623ff40c12SJohn Marino 	}
1633ff40c12SJohn Marino 
1643ff40c12SJohn Marino 	return db;
1653ff40c12SJohn Marino }
1663ff40c12SJohn Marino 
1673ff40c12SJohn Marino 
valid_db_string(const char * str)1683ff40c12SJohn Marino static int valid_db_string(const char *str)
1693ff40c12SJohn Marino {
1703ff40c12SJohn Marino 	const char *pos = str;
1713ff40c12SJohn Marino 	while (*pos) {
1723ff40c12SJohn Marino 		if ((*pos < '0' || *pos > '9') &&
1733ff40c12SJohn Marino 		    (*pos < 'a' || *pos > 'f'))
1743ff40c12SJohn Marino 			return 0;
1753ff40c12SJohn Marino 		pos++;
1763ff40c12SJohn Marino 	}
1773ff40c12SJohn Marino 	return 1;
1783ff40c12SJohn Marino }
1793ff40c12SJohn Marino 
1803ff40c12SJohn Marino 
db_add_pseudonym(struct eap_sim_db_data * data,const char * permanent,char * pseudonym)1813ff40c12SJohn Marino static int db_add_pseudonym(struct eap_sim_db_data *data,
1823ff40c12SJohn Marino 			    const char *permanent, char *pseudonym)
1833ff40c12SJohn Marino {
1843ff40c12SJohn Marino 	char cmd[128];
1853ff40c12SJohn Marino 	char *err = NULL;
1863ff40c12SJohn Marino 
1873ff40c12SJohn Marino 	if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) {
1883ff40c12SJohn Marino 		os_free(pseudonym);
1893ff40c12SJohn Marino 		return -1;
1903ff40c12SJohn Marino 	}
1913ff40c12SJohn Marino 
1923ff40c12SJohn Marino 	os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
1933ff40c12SJohn Marino 		    "(permanent, pseudonym) VALUES ('%s', '%s');",
1943ff40c12SJohn Marino 		    permanent, pseudonym);
1953ff40c12SJohn Marino 	os_free(pseudonym);
1963ff40c12SJohn Marino 	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
1973ff40c12SJohn Marino 	{
1983ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
1993ff40c12SJohn Marino 		sqlite3_free(err);
2003ff40c12SJohn Marino 		return -1;
2013ff40c12SJohn Marino 	}
2023ff40c12SJohn Marino 
2033ff40c12SJohn Marino 	return 0;
2043ff40c12SJohn Marino }
2053ff40c12SJohn Marino 
2063ff40c12SJohn Marino 
get_pseudonym_cb(void * ctx,int argc,char * argv[],char * col[])2073ff40c12SJohn Marino static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
2083ff40c12SJohn Marino {
2093ff40c12SJohn Marino 	struct eap_sim_db_data *data = ctx;
2103ff40c12SJohn Marino 	int i;
2113ff40c12SJohn Marino 
2123ff40c12SJohn Marino 	for (i = 0; i < argc; i++) {
2133ff40c12SJohn Marino 		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
2143ff40c12SJohn Marino 			os_strlcpy(data->db_tmp_identity, argv[i],
2153ff40c12SJohn Marino 				   sizeof(data->db_tmp_identity));
2163ff40c12SJohn Marino 		}
2173ff40c12SJohn Marino 	}
2183ff40c12SJohn Marino 
2193ff40c12SJohn Marino 	return 0;
2203ff40c12SJohn Marino }
2213ff40c12SJohn Marino 
2223ff40c12SJohn Marino 
2233ff40c12SJohn Marino static char *
db_get_pseudonym(struct eap_sim_db_data * data,const char * pseudonym)2243ff40c12SJohn Marino db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
2253ff40c12SJohn Marino {
2263ff40c12SJohn Marino 	char cmd[128];
2273ff40c12SJohn Marino 
2283ff40c12SJohn Marino 	if (!valid_db_string(pseudonym))
2293ff40c12SJohn Marino 		return NULL;
2303ff40c12SJohn Marino 	os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity));
2313ff40c12SJohn Marino 	os_snprintf(cmd, sizeof(cmd),
2323ff40c12SJohn Marino 		    "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';",
2333ff40c12SJohn Marino 		    pseudonym);
2343ff40c12SJohn Marino 	if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
2353ff40c12SJohn Marino 	    SQLITE_OK)
2363ff40c12SJohn Marino 		return NULL;
2373ff40c12SJohn Marino 	if (data->db_tmp_identity[0] == '\0')
2383ff40c12SJohn Marino 		return NULL;
2393ff40c12SJohn Marino 	return data->db_tmp_identity;
2403ff40c12SJohn Marino }
2413ff40c12SJohn Marino 
2423ff40c12SJohn Marino 
db_add_reauth(struct eap_sim_db_data * data,const char * permanent,char * reauth_id,u16 counter,const u8 * mk,const u8 * k_encr,const u8 * k_aut,const u8 * k_re)2433ff40c12SJohn Marino static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
2443ff40c12SJohn Marino 			 char *reauth_id, u16 counter, const u8 *mk,
2453ff40c12SJohn Marino 			 const u8 *k_encr, const u8 *k_aut, const u8 *k_re)
2463ff40c12SJohn Marino {
2473ff40c12SJohn Marino 	char cmd[2000], *pos, *end;
2483ff40c12SJohn Marino 	char *err = NULL;
2493ff40c12SJohn Marino 
2503ff40c12SJohn Marino 	if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) {
2513ff40c12SJohn Marino 		os_free(reauth_id);
2523ff40c12SJohn Marino 		return -1;
2533ff40c12SJohn Marino 	}
2543ff40c12SJohn Marino 
2553ff40c12SJohn Marino 	pos = cmd;
2563ff40c12SJohn Marino 	end = pos + sizeof(cmd);
2573ff40c12SJohn Marino 	pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
2583ff40c12SJohn Marino 			   "(permanent, reauth_id, counter%s%s%s%s) "
2593ff40c12SJohn Marino 			   "VALUES ('%s', '%s', %u",
2603ff40c12SJohn Marino 			   mk ? ", mk" : "",
2613ff40c12SJohn Marino 			   k_encr ? ", k_encr" : "",
2623ff40c12SJohn Marino 			   k_aut ? ", k_aut" : "",
2633ff40c12SJohn Marino 			   k_re ? ", k_re" : "",
2643ff40c12SJohn Marino 			   permanent, reauth_id, counter);
2653ff40c12SJohn Marino 	os_free(reauth_id);
2663ff40c12SJohn Marino 
2673ff40c12SJohn Marino 	if (mk) {
2683ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, ", '");
2693ff40c12SJohn Marino 		pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
2703ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, "'");
2713ff40c12SJohn Marino 	}
2723ff40c12SJohn Marino 
2733ff40c12SJohn Marino 	if (k_encr) {
2743ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, ", '");
2753ff40c12SJohn Marino 		pos += wpa_snprintf_hex(pos, end - pos, k_encr,
2763ff40c12SJohn Marino 					EAP_SIM_K_ENCR_LEN);
2773ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, "'");
2783ff40c12SJohn Marino 	}
2793ff40c12SJohn Marino 
2803ff40c12SJohn Marino 	if (k_aut) {
2813ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, ", '");
2823ff40c12SJohn Marino 		pos += wpa_snprintf_hex(pos, end - pos, k_aut,
2833ff40c12SJohn Marino 					EAP_AKA_PRIME_K_AUT_LEN);
2843ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, "'");
2853ff40c12SJohn Marino 	}
2863ff40c12SJohn Marino 
2873ff40c12SJohn Marino 	if (k_re) {
2883ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, ", '");
2893ff40c12SJohn Marino 		pos += wpa_snprintf_hex(pos, end - pos, k_re,
2903ff40c12SJohn Marino 					EAP_AKA_PRIME_K_RE_LEN);
2913ff40c12SJohn Marino 		pos += os_snprintf(pos, end - pos, "'");
2923ff40c12SJohn Marino 	}
2933ff40c12SJohn Marino 
2943ff40c12SJohn Marino 	os_snprintf(pos, end - pos, ");");
2953ff40c12SJohn Marino 
2963ff40c12SJohn Marino 	if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
2973ff40c12SJohn Marino 	{
2983ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
2993ff40c12SJohn Marino 		sqlite3_free(err);
3003ff40c12SJohn Marino 		return -1;
3013ff40c12SJohn Marino 	}
3023ff40c12SJohn Marino 
3033ff40c12SJohn Marino 	return 0;
3043ff40c12SJohn Marino }
3053ff40c12SJohn Marino 
3063ff40c12SJohn Marino 
get_reauth_cb(void * ctx,int argc,char * argv[],char * col[])3073ff40c12SJohn Marino static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
3083ff40c12SJohn Marino {
3093ff40c12SJohn Marino 	struct eap_sim_db_data *data = ctx;
3103ff40c12SJohn Marino 	int i;
3113ff40c12SJohn Marino 	struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
3123ff40c12SJohn Marino 
3133ff40c12SJohn Marino 	for (i = 0; i < argc; i++) {
3143ff40c12SJohn Marino 		if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
3153ff40c12SJohn Marino 			os_strlcpy(data->db_tmp_identity, argv[i],
3163ff40c12SJohn Marino 				   sizeof(data->db_tmp_identity));
3173ff40c12SJohn Marino 			reauth->permanent = data->db_tmp_identity;
3183ff40c12SJohn Marino 		} else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
3193ff40c12SJohn Marino 			reauth->counter = atoi(argv[i]);
3203ff40c12SJohn Marino 		} else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
3213ff40c12SJohn Marino 			hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
3223ff40c12SJohn Marino 		} else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
3233ff40c12SJohn Marino 			hexstr2bin(argv[i], reauth->k_encr,
3243ff40c12SJohn Marino 				   sizeof(reauth->k_encr));
3253ff40c12SJohn Marino 		} else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
3263ff40c12SJohn Marino 			hexstr2bin(argv[i], reauth->k_aut,
3273ff40c12SJohn Marino 				   sizeof(reauth->k_aut));
3283ff40c12SJohn Marino 		} else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
3293ff40c12SJohn Marino 			hexstr2bin(argv[i], reauth->k_re,
3303ff40c12SJohn Marino 				   sizeof(reauth->k_re));
3313ff40c12SJohn Marino 		}
3323ff40c12SJohn Marino 	}
3333ff40c12SJohn Marino 
3343ff40c12SJohn Marino 	return 0;
3353ff40c12SJohn Marino }
3363ff40c12SJohn Marino 
3373ff40c12SJohn Marino 
3383ff40c12SJohn Marino static struct eap_sim_reauth *
db_get_reauth(struct eap_sim_db_data * data,const char * reauth_id)3393ff40c12SJohn Marino db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
3403ff40c12SJohn Marino {
3413ff40c12SJohn Marino 	char cmd[256];
3423ff40c12SJohn Marino 
3433ff40c12SJohn Marino 	if (!valid_db_string(reauth_id))
3443ff40c12SJohn Marino 		return NULL;
3453ff40c12SJohn Marino 	os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
3463ff40c12SJohn Marino 	os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
3473ff40c12SJohn Marino 		   sizeof(data->db_tmp_pseudonym_str));
3483ff40c12SJohn Marino 	data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
3493ff40c12SJohn Marino 	os_snprintf(cmd, sizeof(cmd),
3503ff40c12SJohn Marino 		    "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
3513ff40c12SJohn Marino 	if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
3523ff40c12SJohn Marino 	    SQLITE_OK)
3533ff40c12SJohn Marino 		return NULL;
3543ff40c12SJohn Marino 	if (data->db_tmp_reauth.permanent == NULL)
3553ff40c12SJohn Marino 		return NULL;
3563ff40c12SJohn Marino 	return &data->db_tmp_reauth;
3573ff40c12SJohn Marino }
3583ff40c12SJohn Marino 
3593ff40c12SJohn Marino 
db_remove_reauth(struct eap_sim_db_data * data,struct eap_sim_reauth * reauth)3603ff40c12SJohn Marino static void db_remove_reauth(struct eap_sim_db_data *data,
3613ff40c12SJohn Marino 			     struct eap_sim_reauth *reauth)
3623ff40c12SJohn Marino {
3633ff40c12SJohn Marino 	char cmd[256];
3643ff40c12SJohn Marino 
3653ff40c12SJohn Marino 	if (!valid_db_string(reauth->permanent))
3663ff40c12SJohn Marino 		return;
3673ff40c12SJohn Marino 	os_snprintf(cmd, sizeof(cmd),
3683ff40c12SJohn Marino 		    "DELETE FROM reauth WHERE permanent='%s';",
3693ff40c12SJohn Marino 		    reauth->permanent);
3703ff40c12SJohn Marino 	sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
3713ff40c12SJohn Marino }
3723ff40c12SJohn Marino 
3733ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
3743ff40c12SJohn Marino 
3753ff40c12SJohn Marino 
3766d49e1aeSJan Lentfer static struct eap_sim_db_pending *
eap_sim_db_get_pending(struct eap_sim_db_data * data,const char * imsi,int aka)3773ff40c12SJohn Marino eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka)
3786d49e1aeSJan Lentfer {
3796d49e1aeSJan Lentfer 	struct eap_sim_db_pending *entry, *prev = NULL;
3806d49e1aeSJan Lentfer 
3816d49e1aeSJan Lentfer 	entry = data->pending;
3826d49e1aeSJan Lentfer 	while (entry) {
3833ff40c12SJohn Marino 		if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) {
3846d49e1aeSJan Lentfer 			if (prev)
3856d49e1aeSJan Lentfer 				prev->next = entry->next;
3866d49e1aeSJan Lentfer 			else
3876d49e1aeSJan Lentfer 				data->pending = entry->next;
3886d49e1aeSJan Lentfer 			break;
3896d49e1aeSJan Lentfer 		}
3906d49e1aeSJan Lentfer 		prev = entry;
3916d49e1aeSJan Lentfer 		entry = entry->next;
3926d49e1aeSJan Lentfer 	}
3936d49e1aeSJan Lentfer 	return entry;
3946d49e1aeSJan Lentfer }
3956d49e1aeSJan Lentfer 
3966d49e1aeSJan Lentfer 
eap_sim_db_add_pending(struct eap_sim_db_data * data,struct eap_sim_db_pending * entry)3976d49e1aeSJan Lentfer static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
3986d49e1aeSJan Lentfer 				   struct eap_sim_db_pending *entry)
3996d49e1aeSJan Lentfer {
4006d49e1aeSJan Lentfer 	entry->next = data->pending;
4016d49e1aeSJan Lentfer 	data->pending = entry;
4026d49e1aeSJan Lentfer }
4036d49e1aeSJan Lentfer 
4046d49e1aeSJan Lentfer 
eap_sim_db_free_pending(struct eap_sim_db_data * data,struct eap_sim_db_pending * entry)405*a1157835SDaniel Fojt static void eap_sim_db_free_pending(struct eap_sim_db_data *data,
406*a1157835SDaniel Fojt 				    struct eap_sim_db_pending *entry)
407*a1157835SDaniel Fojt {
408*a1157835SDaniel Fojt 	eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry);
409*a1157835SDaniel Fojt 	eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry);
410*a1157835SDaniel Fojt 	os_free(entry);
411*a1157835SDaniel Fojt }
412*a1157835SDaniel Fojt 
413*a1157835SDaniel Fojt 
eap_sim_db_del_pending(struct eap_sim_db_data * data,struct eap_sim_db_pending * entry)414*a1157835SDaniel Fojt static void eap_sim_db_del_pending(struct eap_sim_db_data *data,
415*a1157835SDaniel Fojt 				   struct eap_sim_db_pending *entry)
416*a1157835SDaniel Fojt {
417*a1157835SDaniel Fojt 	struct eap_sim_db_pending **pp = &data->pending;
418*a1157835SDaniel Fojt 
419*a1157835SDaniel Fojt 	while (*pp != NULL) {
420*a1157835SDaniel Fojt 		if (*pp == entry) {
421*a1157835SDaniel Fojt 			*pp = entry->next;
422*a1157835SDaniel Fojt 			eap_sim_db_free_pending(data, entry);
423*a1157835SDaniel Fojt 			return;
424*a1157835SDaniel Fojt 		}
425*a1157835SDaniel Fojt 		pp = &(*pp)->next;
426*a1157835SDaniel Fojt 	}
427*a1157835SDaniel Fojt }
428*a1157835SDaniel Fojt 
429*a1157835SDaniel Fojt 
eap_sim_db_del_timeout(void * eloop_ctx,void * user_ctx)430*a1157835SDaniel Fojt static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx)
431*a1157835SDaniel Fojt {
432*a1157835SDaniel Fojt 	struct eap_sim_db_data *data = eloop_ctx;
433*a1157835SDaniel Fojt 	struct eap_sim_db_pending *entry = user_ctx;
434*a1157835SDaniel Fojt 
435*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry);
436*a1157835SDaniel Fojt 	eap_sim_db_del_pending(data, entry);
437*a1157835SDaniel Fojt }
438*a1157835SDaniel Fojt 
439*a1157835SDaniel Fojt 
eap_sim_db_query_timeout(void * eloop_ctx,void * user_ctx)440*a1157835SDaniel Fojt static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx)
441*a1157835SDaniel Fojt {
442*a1157835SDaniel Fojt 	struct eap_sim_db_data *data = eloop_ctx;
443*a1157835SDaniel Fojt 	struct eap_sim_db_pending *entry = user_ctx;
444*a1157835SDaniel Fojt 
445*a1157835SDaniel Fojt 	/*
446*a1157835SDaniel Fojt 	 * Report failure and allow some time for EAP server to process it
447*a1157835SDaniel Fojt 	 * before deleting the query.
448*a1157835SDaniel Fojt 	 */
449*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry);
450*a1157835SDaniel Fojt 	entry->state = FAILURE;
451*a1157835SDaniel Fojt 	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
452*a1157835SDaniel Fojt 	eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry);
453*a1157835SDaniel Fojt }
454*a1157835SDaniel Fojt 
455*a1157835SDaniel Fojt 
eap_sim_db_sim_resp_auth(struct eap_sim_db_data * data,const char * imsi,char * buf)4566d49e1aeSJan Lentfer static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
4576d49e1aeSJan Lentfer 				     const char *imsi, char *buf)
4586d49e1aeSJan Lentfer {
4596d49e1aeSJan Lentfer 	char *start, *end, *pos;
4606d49e1aeSJan Lentfer 	struct eap_sim_db_pending *entry;
4616d49e1aeSJan Lentfer 	int num_chal;
4626d49e1aeSJan Lentfer 
4636d49e1aeSJan Lentfer 	/*
4646d49e1aeSJan Lentfer 	 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
4656d49e1aeSJan Lentfer 	 * SIM-RESP-AUTH <IMSI> FAILURE
4666d49e1aeSJan Lentfer 	 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
4676d49e1aeSJan Lentfer 	 */
4686d49e1aeSJan Lentfer 
4693ff40c12SJohn Marino 	entry = eap_sim_db_get_pending(data, imsi, 0);
4706d49e1aeSJan Lentfer 	if (entry == NULL) {
4716d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
4726d49e1aeSJan Lentfer 			   "received message found");
4736d49e1aeSJan Lentfer 		return;
4746d49e1aeSJan Lentfer 	}
4756d49e1aeSJan Lentfer 
4766d49e1aeSJan Lentfer 	start = buf;
4776d49e1aeSJan Lentfer 	if (os_strncmp(start, "FAILURE", 7) == 0) {
4786d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
4796d49e1aeSJan Lentfer 			   "failure");
4806d49e1aeSJan Lentfer 		entry->state = FAILURE;
4816d49e1aeSJan Lentfer 		eap_sim_db_add_pending(data, entry);
4826d49e1aeSJan Lentfer 		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
4836d49e1aeSJan Lentfer 		return;
4846d49e1aeSJan Lentfer 	}
4856d49e1aeSJan Lentfer 
4866d49e1aeSJan Lentfer 	num_chal = 0;
4876d49e1aeSJan Lentfer 	while (num_chal < EAP_SIM_MAX_CHAL) {
4886d49e1aeSJan Lentfer 		end = os_strchr(start, ' ');
4896d49e1aeSJan Lentfer 		if (end)
4906d49e1aeSJan Lentfer 			*end = '\0';
4916d49e1aeSJan Lentfer 
4926d49e1aeSJan Lentfer 		pos = os_strchr(start, ':');
4936d49e1aeSJan Lentfer 		if (pos == NULL)
4946d49e1aeSJan Lentfer 			goto parse_fail;
4956d49e1aeSJan Lentfer 		*pos = '\0';
4966d49e1aeSJan Lentfer 		if (hexstr2bin(start, entry->u.sim.kc[num_chal],
4976d49e1aeSJan Lentfer 			       EAP_SIM_KC_LEN))
4986d49e1aeSJan Lentfer 			goto parse_fail;
4996d49e1aeSJan Lentfer 
5006d49e1aeSJan Lentfer 		start = pos + 1;
5016d49e1aeSJan Lentfer 		pos = os_strchr(start, ':');
5026d49e1aeSJan Lentfer 		if (pos == NULL)
5036d49e1aeSJan Lentfer 			goto parse_fail;
5046d49e1aeSJan Lentfer 		*pos = '\0';
5056d49e1aeSJan Lentfer 		if (hexstr2bin(start, entry->u.sim.sres[num_chal],
5066d49e1aeSJan Lentfer 			       EAP_SIM_SRES_LEN))
5076d49e1aeSJan Lentfer 			goto parse_fail;
5086d49e1aeSJan Lentfer 
5096d49e1aeSJan Lentfer 		start = pos + 1;
5106d49e1aeSJan Lentfer 		if (hexstr2bin(start, entry->u.sim.rand[num_chal],
5116d49e1aeSJan Lentfer 			       GSM_RAND_LEN))
5126d49e1aeSJan Lentfer 			goto parse_fail;
5136d49e1aeSJan Lentfer 
5146d49e1aeSJan Lentfer 		num_chal++;
5156d49e1aeSJan Lentfer 		if (end == NULL)
5166d49e1aeSJan Lentfer 			break;
5176d49e1aeSJan Lentfer 		else
5186d49e1aeSJan Lentfer 			start = end + 1;
5196d49e1aeSJan Lentfer 	}
5206d49e1aeSJan Lentfer 	entry->u.sim.num_chal = num_chal;
5216d49e1aeSJan Lentfer 
5226d49e1aeSJan Lentfer 	entry->state = SUCCESS;
5236d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
5246d49e1aeSJan Lentfer 		   "successfully - callback");
5256d49e1aeSJan Lentfer 	eap_sim_db_add_pending(data, entry);
5266d49e1aeSJan Lentfer 	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
5276d49e1aeSJan Lentfer 	return;
5286d49e1aeSJan Lentfer 
5296d49e1aeSJan Lentfer parse_fail:
5306d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
531*a1157835SDaniel Fojt 	eap_sim_db_free_pending(data, entry);
5326d49e1aeSJan Lentfer }
5336d49e1aeSJan Lentfer 
5346d49e1aeSJan Lentfer 
eap_sim_db_aka_resp_auth(struct eap_sim_db_data * data,const char * imsi,char * buf)5356d49e1aeSJan Lentfer static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
5366d49e1aeSJan Lentfer 				     const char *imsi, char *buf)
5376d49e1aeSJan Lentfer {
5386d49e1aeSJan Lentfer 	char *start, *end;
5396d49e1aeSJan Lentfer 	struct eap_sim_db_pending *entry;
5406d49e1aeSJan Lentfer 
5416d49e1aeSJan Lentfer 	/*
5426d49e1aeSJan Lentfer 	 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
5436d49e1aeSJan Lentfer 	 * AKA-RESP-AUTH <IMSI> FAILURE
5446d49e1aeSJan Lentfer 	 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
5456d49e1aeSJan Lentfer 	 */
5466d49e1aeSJan Lentfer 
5473ff40c12SJohn Marino 	entry = eap_sim_db_get_pending(data, imsi, 1);
5486d49e1aeSJan Lentfer 	if (entry == NULL) {
5496d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
5506d49e1aeSJan Lentfer 			   "received message found");
5516d49e1aeSJan Lentfer 		return;
5526d49e1aeSJan Lentfer 	}
5536d49e1aeSJan Lentfer 
5546d49e1aeSJan Lentfer 	start = buf;
5556d49e1aeSJan Lentfer 	if (os_strncmp(start, "FAILURE", 7) == 0) {
5566d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
5576d49e1aeSJan Lentfer 			   "failure");
5586d49e1aeSJan Lentfer 		entry->state = FAILURE;
5596d49e1aeSJan Lentfer 		eap_sim_db_add_pending(data, entry);
5606d49e1aeSJan Lentfer 		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
5616d49e1aeSJan Lentfer 		return;
5626d49e1aeSJan Lentfer 	}
5636d49e1aeSJan Lentfer 
5646d49e1aeSJan Lentfer 	end = os_strchr(start, ' ');
5656d49e1aeSJan Lentfer 	if (end == NULL)
5666d49e1aeSJan Lentfer 		goto parse_fail;
5676d49e1aeSJan Lentfer 	*end = '\0';
5686d49e1aeSJan Lentfer 	if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
5696d49e1aeSJan Lentfer 		goto parse_fail;
5706d49e1aeSJan Lentfer 
5716d49e1aeSJan Lentfer 	start = end + 1;
5726d49e1aeSJan Lentfer 	end = os_strchr(start, ' ');
5736d49e1aeSJan Lentfer 	if (end == NULL)
5746d49e1aeSJan Lentfer 		goto parse_fail;
5756d49e1aeSJan Lentfer 	*end = '\0';
5766d49e1aeSJan Lentfer 	if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
5776d49e1aeSJan Lentfer 		goto parse_fail;
5786d49e1aeSJan Lentfer 
5796d49e1aeSJan Lentfer 	start = end + 1;
5806d49e1aeSJan Lentfer 	end = os_strchr(start, ' ');
5816d49e1aeSJan Lentfer 	if (end == NULL)
5826d49e1aeSJan Lentfer 		goto parse_fail;
5836d49e1aeSJan Lentfer 	*end = '\0';
5846d49e1aeSJan Lentfer 	if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
5856d49e1aeSJan Lentfer 		goto parse_fail;
5866d49e1aeSJan Lentfer 
5876d49e1aeSJan Lentfer 	start = end + 1;
5886d49e1aeSJan Lentfer 	end = os_strchr(start, ' ');
5896d49e1aeSJan Lentfer 	if (end == NULL)
5906d49e1aeSJan Lentfer 		goto parse_fail;
5916d49e1aeSJan Lentfer 	*end = '\0';
5926d49e1aeSJan Lentfer 	if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
5936d49e1aeSJan Lentfer 		goto parse_fail;
5946d49e1aeSJan Lentfer 
5956d49e1aeSJan Lentfer 	start = end + 1;
5966d49e1aeSJan Lentfer 	end = os_strchr(start, ' ');
5976d49e1aeSJan Lentfer 	if (end)
5986d49e1aeSJan Lentfer 		*end = '\0';
5996d49e1aeSJan Lentfer 	else {
6006d49e1aeSJan Lentfer 		end = start;
6016d49e1aeSJan Lentfer 		while (*end)
6026d49e1aeSJan Lentfer 			end++;
6036d49e1aeSJan Lentfer 	}
6046d49e1aeSJan Lentfer 	entry->u.aka.res_len = (end - start) / 2;
6056d49e1aeSJan Lentfer 	if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
6066d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
6076d49e1aeSJan Lentfer 		entry->u.aka.res_len = 0;
6086d49e1aeSJan Lentfer 		goto parse_fail;
6096d49e1aeSJan Lentfer 	}
6106d49e1aeSJan Lentfer 	if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
6116d49e1aeSJan Lentfer 		goto parse_fail;
6126d49e1aeSJan Lentfer 
6136d49e1aeSJan Lentfer 	entry->state = SUCCESS;
6146d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
6156d49e1aeSJan Lentfer 		   "successfully - callback");
6166d49e1aeSJan Lentfer 	eap_sim_db_add_pending(data, entry);
6176d49e1aeSJan Lentfer 	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
6186d49e1aeSJan Lentfer 	return;
6196d49e1aeSJan Lentfer 
6206d49e1aeSJan Lentfer parse_fail:
6216d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
622*a1157835SDaniel Fojt 	eap_sim_db_free_pending(data, entry);
6236d49e1aeSJan Lentfer }
6246d49e1aeSJan Lentfer 
6256d49e1aeSJan Lentfer 
eap_sim_db_receive(int sock,void * eloop_ctx,void * sock_ctx)6266d49e1aeSJan Lentfer static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
6276d49e1aeSJan Lentfer {
6286d49e1aeSJan Lentfer 	struct eap_sim_db_data *data = eloop_ctx;
6296d49e1aeSJan Lentfer 	char buf[1000], *pos, *cmd, *imsi;
6306d49e1aeSJan Lentfer 	int res;
6316d49e1aeSJan Lentfer 
632*a1157835SDaniel Fojt 	res = recv(sock, buf, sizeof(buf) - 1, 0);
6336d49e1aeSJan Lentfer 	if (res < 0)
6346d49e1aeSJan Lentfer 		return;
635*a1157835SDaniel Fojt 	buf[res] = '\0';
6366d49e1aeSJan Lentfer 	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
6376d49e1aeSJan Lentfer 			      "external source", (u8 *) buf, res);
6386d49e1aeSJan Lentfer 	if (res == 0)
6396d49e1aeSJan Lentfer 		return;
6406d49e1aeSJan Lentfer 
6416d49e1aeSJan Lentfer 	if (data->get_complete_cb == NULL) {
6426d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
6436d49e1aeSJan Lentfer 			   "registered");
6446d49e1aeSJan Lentfer 		return;
6456d49e1aeSJan Lentfer 	}
6466d49e1aeSJan Lentfer 
6476d49e1aeSJan Lentfer 	/* <cmd> <IMSI> ... */
6486d49e1aeSJan Lentfer 
6496d49e1aeSJan Lentfer 	cmd = buf;
6506d49e1aeSJan Lentfer 	pos = os_strchr(cmd, ' ');
6516d49e1aeSJan Lentfer 	if (pos == NULL)
6526d49e1aeSJan Lentfer 		goto parse_fail;
6536d49e1aeSJan Lentfer 	*pos = '\0';
6546d49e1aeSJan Lentfer 	imsi = pos + 1;
6556d49e1aeSJan Lentfer 	pos = os_strchr(imsi, ' ');
6566d49e1aeSJan Lentfer 	if (pos == NULL)
6576d49e1aeSJan Lentfer 		goto parse_fail;
6586d49e1aeSJan Lentfer 	*pos = '\0';
6596d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
6606d49e1aeSJan Lentfer 		   cmd, imsi);
6616d49e1aeSJan Lentfer 
6626d49e1aeSJan Lentfer 	if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
6636d49e1aeSJan Lentfer 		eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
6646d49e1aeSJan Lentfer 	else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
6656d49e1aeSJan Lentfer 		eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
6666d49e1aeSJan Lentfer 	else
6676d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
6686d49e1aeSJan Lentfer 			   "'%s'", cmd);
6696d49e1aeSJan Lentfer 	return;
6706d49e1aeSJan Lentfer 
6716d49e1aeSJan Lentfer parse_fail:
6726d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
6736d49e1aeSJan Lentfer }
6746d49e1aeSJan Lentfer 
6756d49e1aeSJan Lentfer 
eap_sim_db_open_socket(struct eap_sim_db_data * data)6766d49e1aeSJan Lentfer static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
6776d49e1aeSJan Lentfer {
6786d49e1aeSJan Lentfer 	struct sockaddr_un addr;
6796d49e1aeSJan Lentfer 	static int counter = 0;
6806d49e1aeSJan Lentfer 
6816d49e1aeSJan Lentfer 	if (os_strncmp(data->fname, "unix:", 5) != 0)
6826d49e1aeSJan Lentfer 		return -1;
6836d49e1aeSJan Lentfer 
6846d49e1aeSJan Lentfer 	data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
6856d49e1aeSJan Lentfer 	if (data->sock < 0) {
6863ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno));
6876d49e1aeSJan Lentfer 		return -1;
6886d49e1aeSJan Lentfer 	}
6896d49e1aeSJan Lentfer 
6906d49e1aeSJan Lentfer 	os_memset(&addr, 0, sizeof(addr));
6916d49e1aeSJan Lentfer 	addr.sun_family = AF_UNIX;
6926d49e1aeSJan Lentfer 	os_snprintf(addr.sun_path, sizeof(addr.sun_path),
6936d49e1aeSJan Lentfer 		    "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
6943ff40c12SJohn Marino 	os_free(data->local_sock);
6956d49e1aeSJan Lentfer 	data->local_sock = os_strdup(addr.sun_path);
696*a1157835SDaniel Fojt 	if (data->local_sock == NULL) {
697*a1157835SDaniel Fojt 		close(data->sock);
698*a1157835SDaniel Fojt 		data->sock = -1;
699*a1157835SDaniel Fojt 		return -1;
700*a1157835SDaniel Fojt 	}
7016d49e1aeSJan Lentfer 	if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
7023ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno));
7036d49e1aeSJan Lentfer 		close(data->sock);
7046d49e1aeSJan Lentfer 		data->sock = -1;
7056d49e1aeSJan Lentfer 		return -1;
7066d49e1aeSJan Lentfer 	}
7076d49e1aeSJan Lentfer 
7086d49e1aeSJan Lentfer 	os_memset(&addr, 0, sizeof(addr));
7096d49e1aeSJan Lentfer 	addr.sun_family = AF_UNIX;
7106d49e1aeSJan Lentfer 	os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
7116d49e1aeSJan Lentfer 	if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
7123ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "connect(eap_sim_db): %s",
7133ff40c12SJohn Marino 			   strerror(errno));
7146d49e1aeSJan Lentfer 		wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
7156d49e1aeSJan Lentfer 				  (u8 *) addr.sun_path,
7166d49e1aeSJan Lentfer 				  os_strlen(addr.sun_path));
7176d49e1aeSJan Lentfer 		close(data->sock);
7186d49e1aeSJan Lentfer 		data->sock = -1;
719*a1157835SDaniel Fojt 		unlink(data->local_sock);
720*a1157835SDaniel Fojt 		os_free(data->local_sock);
721*a1157835SDaniel Fojt 		data->local_sock = NULL;
7226d49e1aeSJan Lentfer 		return -1;
7236d49e1aeSJan Lentfer 	}
7246d49e1aeSJan Lentfer 
7256d49e1aeSJan Lentfer 	eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
7266d49e1aeSJan Lentfer 
7276d49e1aeSJan Lentfer 	return 0;
7286d49e1aeSJan Lentfer }
7296d49e1aeSJan Lentfer 
7306d49e1aeSJan Lentfer 
eap_sim_db_close_socket(struct eap_sim_db_data * data)7316d49e1aeSJan Lentfer static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
7326d49e1aeSJan Lentfer {
7336d49e1aeSJan Lentfer 	if (data->sock >= 0) {
7346d49e1aeSJan Lentfer 		eloop_unregister_read_sock(data->sock);
7356d49e1aeSJan Lentfer 		close(data->sock);
7366d49e1aeSJan Lentfer 		data->sock = -1;
7376d49e1aeSJan Lentfer 	}
7386d49e1aeSJan Lentfer 	if (data->local_sock) {
7396d49e1aeSJan Lentfer 		unlink(data->local_sock);
7406d49e1aeSJan Lentfer 		os_free(data->local_sock);
7416d49e1aeSJan Lentfer 		data->local_sock = NULL;
7426d49e1aeSJan Lentfer 	}
7436d49e1aeSJan Lentfer }
7446d49e1aeSJan Lentfer 
7456d49e1aeSJan Lentfer 
7466d49e1aeSJan Lentfer /**
7476d49e1aeSJan Lentfer  * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
7486d49e1aeSJan Lentfer  * @config: Configuration data (e.g., file name)
749*a1157835SDaniel Fojt  * @db_timeout: Database lookup timeout
7506d49e1aeSJan Lentfer  * @get_complete_cb: Callback function for reporting availability of triplets
7516d49e1aeSJan Lentfer  * @ctx: Context pointer for get_complete_cb
7526d49e1aeSJan Lentfer  * Returns: Pointer to a private data structure or %NULL on failure
7536d49e1aeSJan Lentfer  */
7543ff40c12SJohn Marino struct eap_sim_db_data *
eap_sim_db_init(const char * config,unsigned int db_timeout,void (* get_complete_cb)(void * ctx,void * session_ctx),void * ctx)755*a1157835SDaniel Fojt eap_sim_db_init(const char *config, unsigned int db_timeout,
7566d49e1aeSJan Lentfer 		void (*get_complete_cb)(void *ctx, void *session_ctx),
7576d49e1aeSJan Lentfer 		void *ctx)
7586d49e1aeSJan Lentfer {
7596d49e1aeSJan Lentfer 	struct eap_sim_db_data *data;
7603ff40c12SJohn Marino 	char *pos;
7616d49e1aeSJan Lentfer 
7626d49e1aeSJan Lentfer 	data = os_zalloc(sizeof(*data));
7636d49e1aeSJan Lentfer 	if (data == NULL)
7646d49e1aeSJan Lentfer 		return NULL;
7656d49e1aeSJan Lentfer 
7666d49e1aeSJan Lentfer 	data->sock = -1;
7676d49e1aeSJan Lentfer 	data->get_complete_cb = get_complete_cb;
7686d49e1aeSJan Lentfer 	data->ctx = ctx;
769*a1157835SDaniel Fojt 	data->eap_sim_db_timeout = db_timeout;
7706d49e1aeSJan Lentfer 	data->fname = os_strdup(config);
7716d49e1aeSJan Lentfer 	if (data->fname == NULL)
7726d49e1aeSJan Lentfer 		goto fail;
7733ff40c12SJohn Marino 	pos = os_strstr(data->fname, " db=");
7743ff40c12SJohn Marino 	if (pos) {
7753ff40c12SJohn Marino 		*pos = '\0';
7763ff40c12SJohn Marino #ifdef CONFIG_SQLITE
7773ff40c12SJohn Marino 		pos += 4;
7783ff40c12SJohn Marino 		data->sqlite_db = db_open(pos);
7793ff40c12SJohn Marino 		if (data->sqlite_db == NULL)
7803ff40c12SJohn Marino 			goto fail;
7813ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
7823ff40c12SJohn Marino 	}
7836d49e1aeSJan Lentfer 
7846d49e1aeSJan Lentfer 	if (os_strncmp(data->fname, "unix:", 5) == 0) {
7853ff40c12SJohn Marino 		if (eap_sim_db_open_socket(data)) {
7863ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database "
7873ff40c12SJohn Marino 				   "connection not available - will retry "
7883ff40c12SJohn Marino 				   "later");
7893ff40c12SJohn Marino 		}
7906d49e1aeSJan Lentfer 	}
7916d49e1aeSJan Lentfer 
7926d49e1aeSJan Lentfer 	return data;
7936d49e1aeSJan Lentfer 
7946d49e1aeSJan Lentfer fail:
7956d49e1aeSJan Lentfer 	eap_sim_db_close_socket(data);
7966d49e1aeSJan Lentfer 	os_free(data->fname);
7976d49e1aeSJan Lentfer 	os_free(data);
7986d49e1aeSJan Lentfer 	return NULL;
7996d49e1aeSJan Lentfer }
8006d49e1aeSJan Lentfer 
8016d49e1aeSJan Lentfer 
eap_sim_db_free_pseudonym(struct eap_sim_pseudonym * p)8026d49e1aeSJan Lentfer static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
8036d49e1aeSJan Lentfer {
8043ff40c12SJohn Marino 	os_free(p->permanent);
8056d49e1aeSJan Lentfer 	os_free(p->pseudonym);
8066d49e1aeSJan Lentfer 	os_free(p);
8076d49e1aeSJan Lentfer }
8086d49e1aeSJan Lentfer 
8096d49e1aeSJan Lentfer 
eap_sim_db_free_reauth(struct eap_sim_reauth * r)8106d49e1aeSJan Lentfer static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
8116d49e1aeSJan Lentfer {
8123ff40c12SJohn Marino 	os_free(r->permanent);
8136d49e1aeSJan Lentfer 	os_free(r->reauth_id);
8146d49e1aeSJan Lentfer 	os_free(r);
8156d49e1aeSJan Lentfer }
8166d49e1aeSJan Lentfer 
8176d49e1aeSJan Lentfer 
8186d49e1aeSJan Lentfer /**
8196d49e1aeSJan Lentfer  * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
8206d49e1aeSJan Lentfer  * @priv: Private data pointer from eap_sim_db_init()
8216d49e1aeSJan Lentfer  */
eap_sim_db_deinit(void * priv)8226d49e1aeSJan Lentfer void eap_sim_db_deinit(void *priv)
8236d49e1aeSJan Lentfer {
8246d49e1aeSJan Lentfer 	struct eap_sim_db_data *data = priv;
8256d49e1aeSJan Lentfer 	struct eap_sim_pseudonym *p, *prev;
8266d49e1aeSJan Lentfer 	struct eap_sim_reauth *r, *prevr;
8276d49e1aeSJan Lentfer 	struct eap_sim_db_pending *pending, *prev_pending;
8286d49e1aeSJan Lentfer 
8293ff40c12SJohn Marino #ifdef CONFIG_SQLITE
8303ff40c12SJohn Marino 	if (data->sqlite_db) {
8313ff40c12SJohn Marino 		sqlite3_close(data->sqlite_db);
8323ff40c12SJohn Marino 		data->sqlite_db = NULL;
8333ff40c12SJohn Marino 	}
8343ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
8353ff40c12SJohn Marino 
8366d49e1aeSJan Lentfer 	eap_sim_db_close_socket(data);
8376d49e1aeSJan Lentfer 	os_free(data->fname);
8386d49e1aeSJan Lentfer 
8396d49e1aeSJan Lentfer 	p = data->pseudonyms;
8406d49e1aeSJan Lentfer 	while (p) {
8416d49e1aeSJan Lentfer 		prev = p;
8426d49e1aeSJan Lentfer 		p = p->next;
8436d49e1aeSJan Lentfer 		eap_sim_db_free_pseudonym(prev);
8446d49e1aeSJan Lentfer 	}
8456d49e1aeSJan Lentfer 
8466d49e1aeSJan Lentfer 	r = data->reauths;
8476d49e1aeSJan Lentfer 	while (r) {
8486d49e1aeSJan Lentfer 		prevr = r;
8496d49e1aeSJan Lentfer 		r = r->next;
8506d49e1aeSJan Lentfer 		eap_sim_db_free_reauth(prevr);
8516d49e1aeSJan Lentfer 	}
8526d49e1aeSJan Lentfer 
8536d49e1aeSJan Lentfer 	pending = data->pending;
8546d49e1aeSJan Lentfer 	while (pending) {
8556d49e1aeSJan Lentfer 		prev_pending = pending;
8566d49e1aeSJan Lentfer 		pending = pending->next;
857*a1157835SDaniel Fojt 		eap_sim_db_free_pending(data, prev_pending);
8586d49e1aeSJan Lentfer 	}
8596d49e1aeSJan Lentfer 
8606d49e1aeSJan Lentfer 	os_free(data);
8616d49e1aeSJan Lentfer }
8626d49e1aeSJan Lentfer 
8636d49e1aeSJan Lentfer 
eap_sim_db_send(struct eap_sim_db_data * data,const char * msg,size_t len)8646d49e1aeSJan Lentfer static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
8656d49e1aeSJan Lentfer 			   size_t len)
8666d49e1aeSJan Lentfer {
8676d49e1aeSJan Lentfer 	int _errno = 0;
8686d49e1aeSJan Lentfer 
8696d49e1aeSJan Lentfer 	if (send(data->sock, msg, len, 0) < 0) {
8706d49e1aeSJan Lentfer 		_errno = errno;
8713ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
8723ff40c12SJohn Marino 			   strerror(errno));
8736d49e1aeSJan Lentfer 	}
8746d49e1aeSJan Lentfer 
8756d49e1aeSJan Lentfer 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
8766d49e1aeSJan Lentfer 	    _errno == ECONNREFUSED) {
8776d49e1aeSJan Lentfer 		/* Try to reconnect */
8786d49e1aeSJan Lentfer 		eap_sim_db_close_socket(data);
8796d49e1aeSJan Lentfer 		if (eap_sim_db_open_socket(data) < 0)
8806d49e1aeSJan Lentfer 			return -1;
8816d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
8826d49e1aeSJan Lentfer 			   "external server");
8836d49e1aeSJan Lentfer 		if (send(data->sock, msg, len, 0) < 0) {
8843ff40c12SJohn Marino 			wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
8853ff40c12SJohn Marino 				   strerror(errno));
8866d49e1aeSJan Lentfer 			return -1;
8876d49e1aeSJan Lentfer 		}
8886d49e1aeSJan Lentfer 	}
8896d49e1aeSJan Lentfer 
8906d49e1aeSJan Lentfer 	return 0;
8916d49e1aeSJan Lentfer }
8926d49e1aeSJan Lentfer 
8936d49e1aeSJan Lentfer 
eap_sim_db_expire_pending(struct eap_sim_db_data * data,struct eap_sim_db_pending * entry)894*a1157835SDaniel Fojt static void eap_sim_db_expire_pending(struct eap_sim_db_data *data,
895*a1157835SDaniel Fojt 				      struct eap_sim_db_pending *entry)
8966d49e1aeSJan Lentfer {
897*a1157835SDaniel Fojt 	eloop_register_timeout(data->eap_sim_db_timeout, 0,
898*a1157835SDaniel Fojt 			       eap_sim_db_query_timeout, data, entry);
8996d49e1aeSJan Lentfer }
9006d49e1aeSJan Lentfer 
9016d49e1aeSJan Lentfer 
9026d49e1aeSJan Lentfer /**
9036d49e1aeSJan Lentfer  * eap_sim_db_get_gsm_triplets - Get GSM triplets
9043ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
9053ff40c12SJohn Marino  * @username: Permanent username (prefix | IMSI)
9066d49e1aeSJan Lentfer  * @max_chal: Maximum number of triplets
9076d49e1aeSJan Lentfer  * @_rand: Buffer for RAND values
9086d49e1aeSJan Lentfer  * @kc: Buffer for Kc values
9096d49e1aeSJan Lentfer  * @sres: Buffer for SRES values
9106d49e1aeSJan Lentfer  * @cb_session_ctx: Session callback context for get_complete_cb()
9116d49e1aeSJan Lentfer  * Returns: Number of triplets received (has to be less than or equal to
9126d49e1aeSJan Lentfer  * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
9136d49e1aeSJan Lentfer  * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
9146d49e1aeSJan Lentfer  * callback function registered with eap_sim_db_init() will be called once the
9156d49e1aeSJan Lentfer  * results become available.
9166d49e1aeSJan Lentfer  *
9176d49e1aeSJan Lentfer  * When using an external server for GSM triplets, this function can always
9186d49e1aeSJan Lentfer  * start a request and return EAP_SIM_DB_PENDING immediately if authentication
9196d49e1aeSJan Lentfer  * triplets are not available. Once the triplets are received, callback
9206d49e1aeSJan Lentfer  * function registered with eap_sim_db_init() is called to notify EAP state
9216d49e1aeSJan Lentfer  * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
9226d49e1aeSJan Lentfer  * function will then be called again and the newly received triplets will then
9236d49e1aeSJan Lentfer  * be given to the caller.
9246d49e1aeSJan Lentfer  */
eap_sim_db_get_gsm_triplets(struct eap_sim_db_data * data,const char * username,int max_chal,u8 * _rand,u8 * kc,u8 * sres,void * cb_session_ctx)9253ff40c12SJohn Marino int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
9263ff40c12SJohn Marino 				const char *username, int max_chal,
9276d49e1aeSJan Lentfer 				u8 *_rand, u8 *kc, u8 *sres,
9286d49e1aeSJan Lentfer 				void *cb_session_ctx)
9296d49e1aeSJan Lentfer {
9306d49e1aeSJan Lentfer 	struct eap_sim_db_pending *entry;
9316d49e1aeSJan Lentfer 	int len, ret;
9326d49e1aeSJan Lentfer 	char msg[40];
9333ff40c12SJohn Marino 	const char *imsi;
9343ff40c12SJohn Marino 	size_t imsi_len;
9356d49e1aeSJan Lentfer 
9363ff40c12SJohn Marino 	if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX ||
9373ff40c12SJohn Marino 	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
9383ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
9393ff40c12SJohn Marino 			   username);
9406d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
9416d49e1aeSJan Lentfer 	}
9423ff40c12SJohn Marino 	imsi = username + 1;
9433ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'",
9443ff40c12SJohn Marino 		   imsi);
9456d49e1aeSJan Lentfer 
9463ff40c12SJohn Marino 	entry = eap_sim_db_get_pending(data, imsi, 0);
9476d49e1aeSJan Lentfer 	if (entry) {
9486d49e1aeSJan Lentfer 		int num_chal;
9496d49e1aeSJan Lentfer 		if (entry->state == FAILURE) {
9506d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
9516d49e1aeSJan Lentfer 				   "failure");
952*a1157835SDaniel Fojt 			eap_sim_db_free_pending(data, entry);
9536d49e1aeSJan Lentfer 			return EAP_SIM_DB_FAILURE;
9546d49e1aeSJan Lentfer 		}
9556d49e1aeSJan Lentfer 
9566d49e1aeSJan Lentfer 		if (entry->state == PENDING) {
9576d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
9586d49e1aeSJan Lentfer 				   "still pending");
9596d49e1aeSJan Lentfer 			eap_sim_db_add_pending(data, entry);
9606d49e1aeSJan Lentfer 			return EAP_SIM_DB_PENDING;
9616d49e1aeSJan Lentfer 		}
9626d49e1aeSJan Lentfer 
9636d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
9646d49e1aeSJan Lentfer 			   "%d challenges", entry->u.sim.num_chal);
9656d49e1aeSJan Lentfer 		num_chal = entry->u.sim.num_chal;
9666d49e1aeSJan Lentfer 		if (num_chal > max_chal)
9676d49e1aeSJan Lentfer 			num_chal = max_chal;
9686d49e1aeSJan Lentfer 		os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
9696d49e1aeSJan Lentfer 		os_memcpy(sres, entry->u.sim.sres,
9706d49e1aeSJan Lentfer 			  num_chal * EAP_SIM_SRES_LEN);
9716d49e1aeSJan Lentfer 		os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
972*a1157835SDaniel Fojt 		eap_sim_db_free_pending(data, entry);
9736d49e1aeSJan Lentfer 		return num_chal;
9746d49e1aeSJan Lentfer 	}
9756d49e1aeSJan Lentfer 
9766d49e1aeSJan Lentfer 	if (data->sock < 0) {
9776d49e1aeSJan Lentfer 		if (eap_sim_db_open_socket(data) < 0)
9786d49e1aeSJan Lentfer 			return EAP_SIM_DB_FAILURE;
9796d49e1aeSJan Lentfer 	}
9806d49e1aeSJan Lentfer 
9813ff40c12SJohn Marino 	imsi_len = os_strlen(imsi);
9826d49e1aeSJan Lentfer 	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
983*a1157835SDaniel Fojt 	if (os_snprintf_error(sizeof(msg), len) ||
984*a1157835SDaniel Fojt 	    len + imsi_len >= sizeof(msg))
9856d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
9863ff40c12SJohn Marino 	os_memcpy(msg + len, imsi, imsi_len);
9873ff40c12SJohn Marino 	len += imsi_len;
9886d49e1aeSJan Lentfer 	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
989*a1157835SDaniel Fojt 	if (os_snprintf_error(sizeof(msg) - len, ret))
9906d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
9916d49e1aeSJan Lentfer 	len += ret;
9926d49e1aeSJan Lentfer 
9933ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
9943ff40c12SJohn Marino 		   "data for IMSI '%s'", imsi);
9956d49e1aeSJan Lentfer 	if (eap_sim_db_send(data, msg, len) < 0)
9966d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
9976d49e1aeSJan Lentfer 
9986d49e1aeSJan Lentfer 	entry = os_zalloc(sizeof(*entry));
9996d49e1aeSJan Lentfer 	if (entry == NULL)
10006d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
10016d49e1aeSJan Lentfer 
10023ff40c12SJohn Marino 	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
10036d49e1aeSJan Lentfer 	entry->cb_session_ctx = cb_session_ctx;
10046d49e1aeSJan Lentfer 	entry->state = PENDING;
10056d49e1aeSJan Lentfer 	eap_sim_db_add_pending(data, entry);
1006*a1157835SDaniel Fojt 	eap_sim_db_expire_pending(data, entry);
1007*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
10086d49e1aeSJan Lentfer 
10096d49e1aeSJan Lentfer 	return EAP_SIM_DB_PENDING;
10106d49e1aeSJan Lentfer }
10116d49e1aeSJan Lentfer 
10126d49e1aeSJan Lentfer 
eap_sim_db_get_next(struct eap_sim_db_data * data,char prefix)10136d49e1aeSJan Lentfer static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
10146d49e1aeSJan Lentfer {
10156d49e1aeSJan Lentfer 	char *id, *pos, *end;
10166d49e1aeSJan Lentfer 	u8 buf[10];
10176d49e1aeSJan Lentfer 
10183ff40c12SJohn Marino 	if (random_get_bytes(buf, sizeof(buf)))
10196d49e1aeSJan Lentfer 		return NULL;
10206d49e1aeSJan Lentfer 	id = os_malloc(sizeof(buf) * 2 + 2);
10216d49e1aeSJan Lentfer 	if (id == NULL)
10226d49e1aeSJan Lentfer 		return NULL;
10236d49e1aeSJan Lentfer 
10246d49e1aeSJan Lentfer 	pos = id;
10256d49e1aeSJan Lentfer 	end = id + sizeof(buf) * 2 + 2;
10266d49e1aeSJan Lentfer 	*pos++ = prefix;
1027*a1157835SDaniel Fojt 	wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
10286d49e1aeSJan Lentfer 
10296d49e1aeSJan Lentfer 	return id;
10306d49e1aeSJan Lentfer }
10316d49e1aeSJan Lentfer 
10326d49e1aeSJan Lentfer 
10336d49e1aeSJan Lentfer /**
10346d49e1aeSJan Lentfer  * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
10353ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
10363ff40c12SJohn Marino  * @method: EAP method (SIM/AKA/AKA')
10376d49e1aeSJan Lentfer  * Returns: Next pseudonym (allocated string) or %NULL on failure
10386d49e1aeSJan Lentfer  *
10396d49e1aeSJan Lentfer  * This function is used to generate a pseudonym for EAP-SIM. The returned
10406d49e1aeSJan Lentfer  * pseudonym is not added to database at this point; it will need to be added
10416d49e1aeSJan Lentfer  * with eap_sim_db_add_pseudonym() once the authentication has been completed
10426d49e1aeSJan Lentfer  * successfully. Caller is responsible for freeing the returned buffer.
10436d49e1aeSJan Lentfer  */
eap_sim_db_get_next_pseudonym(struct eap_sim_db_data * data,enum eap_sim_db_method method)10443ff40c12SJohn Marino char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
10453ff40c12SJohn Marino 				     enum eap_sim_db_method method)
10466d49e1aeSJan Lentfer {
10473ff40c12SJohn Marino 	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
10483ff40c12SJohn Marino 
10493ff40c12SJohn Marino 	switch (method) {
10503ff40c12SJohn Marino 	case EAP_SIM_DB_SIM:
10513ff40c12SJohn Marino 		prefix = EAP_SIM_PSEUDONYM_PREFIX;
10523ff40c12SJohn Marino 		break;
10533ff40c12SJohn Marino 	case EAP_SIM_DB_AKA:
10543ff40c12SJohn Marino 		prefix = EAP_AKA_PSEUDONYM_PREFIX;
10553ff40c12SJohn Marino 		break;
10563ff40c12SJohn Marino 	case EAP_SIM_DB_AKA_PRIME:
10573ff40c12SJohn Marino 		prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
10583ff40c12SJohn Marino 		break;
10593ff40c12SJohn Marino 	}
10603ff40c12SJohn Marino 
10613ff40c12SJohn Marino 	return eap_sim_db_get_next(data, prefix);
10626d49e1aeSJan Lentfer }
10636d49e1aeSJan Lentfer 
10646d49e1aeSJan Lentfer 
10656d49e1aeSJan Lentfer /**
10666d49e1aeSJan Lentfer  * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
10673ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
10683ff40c12SJohn Marino  * @method: EAP method (SIM/AKA/AKA')
10696d49e1aeSJan Lentfer  * Returns: Next reauth_id (allocated string) or %NULL on failure
10706d49e1aeSJan Lentfer  *
10716d49e1aeSJan Lentfer  * This function is used to generate a fast re-authentication identity for
10726d49e1aeSJan Lentfer  * EAP-SIM. The returned reauth_id is not added to database at this point; it
10736d49e1aeSJan Lentfer  * will need to be added with eap_sim_db_add_reauth() once the authentication
10746d49e1aeSJan Lentfer  * has been completed successfully. Caller is responsible for freeing the
10756d49e1aeSJan Lentfer  * returned buffer.
10766d49e1aeSJan Lentfer  */
eap_sim_db_get_next_reauth_id(struct eap_sim_db_data * data,enum eap_sim_db_method method)10773ff40c12SJohn Marino char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
10783ff40c12SJohn Marino 				     enum eap_sim_db_method method)
10796d49e1aeSJan Lentfer {
10803ff40c12SJohn Marino 	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
10813ff40c12SJohn Marino 
10823ff40c12SJohn Marino 	switch (method) {
10833ff40c12SJohn Marino 	case EAP_SIM_DB_SIM:
10843ff40c12SJohn Marino 		prefix = EAP_SIM_REAUTH_ID_PREFIX;
10853ff40c12SJohn Marino 		break;
10863ff40c12SJohn Marino 	case EAP_SIM_DB_AKA:
10873ff40c12SJohn Marino 		prefix = EAP_AKA_REAUTH_ID_PREFIX;
10883ff40c12SJohn Marino 		break;
10893ff40c12SJohn Marino 	case EAP_SIM_DB_AKA_PRIME:
10903ff40c12SJohn Marino 		prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
10913ff40c12SJohn Marino 		break;
10923ff40c12SJohn Marino 	}
10933ff40c12SJohn Marino 
10943ff40c12SJohn Marino 	return eap_sim_db_get_next(data, prefix);
10956d49e1aeSJan Lentfer }
10966d49e1aeSJan Lentfer 
10976d49e1aeSJan Lentfer 
10986d49e1aeSJan Lentfer /**
10996d49e1aeSJan Lentfer  * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
11003ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
11013ff40c12SJohn Marino  * @permanent: Permanent username
11026d49e1aeSJan Lentfer  * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
11036d49e1aeSJan Lentfer  * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
11046d49e1aeSJan Lentfer  * free it.
11056d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
11066d49e1aeSJan Lentfer  *
11076d49e1aeSJan Lentfer  * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
11086d49e1aeSJan Lentfer  * responsible of freeing pseudonym buffer once it is not needed anymore.
11096d49e1aeSJan Lentfer  */
eap_sim_db_add_pseudonym(struct eap_sim_db_data * data,const char * permanent,char * pseudonym)11103ff40c12SJohn Marino int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
11113ff40c12SJohn Marino 			     const char *permanent, char *pseudonym)
11126d49e1aeSJan Lentfer {
11136d49e1aeSJan Lentfer 	struct eap_sim_pseudonym *p;
11143ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent "
11153ff40c12SJohn Marino 		   "username '%s'", pseudonym, permanent);
11166d49e1aeSJan Lentfer 
11176d49e1aeSJan Lentfer 	/* TODO: could store last two pseudonyms */
11183ff40c12SJohn Marino #ifdef CONFIG_SQLITE
11193ff40c12SJohn Marino 	if (data->sqlite_db)
11203ff40c12SJohn Marino 		return db_add_pseudonym(data, permanent, pseudonym);
11213ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
11223ff40c12SJohn Marino 	for (p = data->pseudonyms; p; p = p->next) {
11233ff40c12SJohn Marino 		if (os_strcmp(permanent, p->permanent) == 0)
11243ff40c12SJohn Marino 			break;
11253ff40c12SJohn Marino 	}
11266d49e1aeSJan Lentfer 	if (p) {
11276d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
11286d49e1aeSJan Lentfer 			   "pseudonym: %s", p->pseudonym);
11296d49e1aeSJan Lentfer 		os_free(p->pseudonym);
11306d49e1aeSJan Lentfer 		p->pseudonym = pseudonym;
11316d49e1aeSJan Lentfer 		return 0;
11326d49e1aeSJan Lentfer 	}
11336d49e1aeSJan Lentfer 
11346d49e1aeSJan Lentfer 	p = os_zalloc(sizeof(*p));
11356d49e1aeSJan Lentfer 	if (p == NULL) {
11366d49e1aeSJan Lentfer 		os_free(pseudonym);
11376d49e1aeSJan Lentfer 		return -1;
11386d49e1aeSJan Lentfer 	}
11396d49e1aeSJan Lentfer 
11406d49e1aeSJan Lentfer 	p->next = data->pseudonyms;
11413ff40c12SJohn Marino 	p->permanent = os_strdup(permanent);
11423ff40c12SJohn Marino 	if (p->permanent == NULL) {
11436d49e1aeSJan Lentfer 		os_free(p);
11446d49e1aeSJan Lentfer 		os_free(pseudonym);
11456d49e1aeSJan Lentfer 		return -1;
11466d49e1aeSJan Lentfer 	}
11476d49e1aeSJan Lentfer 	p->pseudonym = pseudonym;
11486d49e1aeSJan Lentfer 	data->pseudonyms = p;
11496d49e1aeSJan Lentfer 
11506d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
11516d49e1aeSJan Lentfer 	return 0;
11526d49e1aeSJan Lentfer }
11536d49e1aeSJan Lentfer 
11546d49e1aeSJan Lentfer 
11556d49e1aeSJan Lentfer static struct eap_sim_reauth *
eap_sim_db_add_reauth_data(struct eap_sim_db_data * data,const char * permanent,char * reauth_id,u16 counter)11563ff40c12SJohn Marino eap_sim_db_add_reauth_data(struct eap_sim_db_data *data,
11573ff40c12SJohn Marino 			   const char *permanent,
11583ff40c12SJohn Marino 			   char *reauth_id, u16 counter)
11596d49e1aeSJan Lentfer {
11606d49e1aeSJan Lentfer 	struct eap_sim_reauth *r;
11616d49e1aeSJan Lentfer 
11623ff40c12SJohn Marino 	for (r = data->reauths; r; r = r->next) {
11633ff40c12SJohn Marino 		if (os_strcmp(r->permanent, permanent) == 0)
11643ff40c12SJohn Marino 			break;
11653ff40c12SJohn Marino 	}
11666d49e1aeSJan Lentfer 
11676d49e1aeSJan Lentfer 	if (r) {
11686d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
11696d49e1aeSJan Lentfer 			   "reauth_id: %s", r->reauth_id);
11706d49e1aeSJan Lentfer 		os_free(r->reauth_id);
11716d49e1aeSJan Lentfer 		r->reauth_id = reauth_id;
11726d49e1aeSJan Lentfer 	} else {
11736d49e1aeSJan Lentfer 		r = os_zalloc(sizeof(*r));
11746d49e1aeSJan Lentfer 		if (r == NULL) {
11756d49e1aeSJan Lentfer 			os_free(reauth_id);
11766d49e1aeSJan Lentfer 			return NULL;
11776d49e1aeSJan Lentfer 		}
11786d49e1aeSJan Lentfer 
11796d49e1aeSJan Lentfer 		r->next = data->reauths;
11803ff40c12SJohn Marino 		r->permanent = os_strdup(permanent);
11813ff40c12SJohn Marino 		if (r->permanent == NULL) {
11826d49e1aeSJan Lentfer 			os_free(r);
11836d49e1aeSJan Lentfer 			os_free(reauth_id);
11846d49e1aeSJan Lentfer 			return NULL;
11856d49e1aeSJan Lentfer 		}
11866d49e1aeSJan Lentfer 		r->reauth_id = reauth_id;
11876d49e1aeSJan Lentfer 		data->reauths = r;
11886d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
11896d49e1aeSJan Lentfer 	}
11906d49e1aeSJan Lentfer 
11916d49e1aeSJan Lentfer 	r->counter = counter;
11926d49e1aeSJan Lentfer 
11936d49e1aeSJan Lentfer 	return r;
11946d49e1aeSJan Lentfer }
11956d49e1aeSJan Lentfer 
11966d49e1aeSJan Lentfer 
11976d49e1aeSJan Lentfer /**
11986d49e1aeSJan Lentfer  * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
11996d49e1aeSJan Lentfer  * @priv: Private data pointer from eap_sim_db_init()
12003ff40c12SJohn Marino  * @permanent: Permanent username
12016d49e1aeSJan Lentfer  * @identity_len: Length of identity
12026d49e1aeSJan Lentfer  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
12036d49e1aeSJan Lentfer  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
12046d49e1aeSJan Lentfer  * free it.
12056d49e1aeSJan Lentfer  * @counter: AT_COUNTER value for fast re-authentication
12066d49e1aeSJan Lentfer  * @mk: 16-byte MK from the previous full authentication or %NULL
12076d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
12086d49e1aeSJan Lentfer  *
12096d49e1aeSJan Lentfer  * This function adds a new re-authentication entry for an EAP-SIM user.
12106d49e1aeSJan Lentfer  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
12116d49e1aeSJan Lentfer  * anymore.
12126d49e1aeSJan Lentfer  */
eap_sim_db_add_reauth(struct eap_sim_db_data * data,const char * permanent,char * reauth_id,u16 counter,const u8 * mk)12133ff40c12SJohn Marino int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
12143ff40c12SJohn Marino 			  char *reauth_id, u16 counter, const u8 *mk)
12156d49e1aeSJan Lentfer {
12166d49e1aeSJan Lentfer 	struct eap_sim_reauth *r;
12176d49e1aeSJan Lentfer 
12183ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
12193ff40c12SJohn Marino 		   "identity '%s'", reauth_id, permanent);
12203ff40c12SJohn Marino 
12213ff40c12SJohn Marino #ifdef CONFIG_SQLITE
12223ff40c12SJohn Marino 	if (data->sqlite_db)
12233ff40c12SJohn Marino 		return db_add_reauth(data, permanent, reauth_id, counter, mk,
12243ff40c12SJohn Marino 				     NULL, NULL, NULL);
12253ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
12263ff40c12SJohn Marino 	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
12276d49e1aeSJan Lentfer 	if (r == NULL)
12286d49e1aeSJan Lentfer 		return -1;
12296d49e1aeSJan Lentfer 
12306d49e1aeSJan Lentfer 	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
12316d49e1aeSJan Lentfer 
12326d49e1aeSJan Lentfer 	return 0;
12336d49e1aeSJan Lentfer }
12346d49e1aeSJan Lentfer 
12356d49e1aeSJan Lentfer 
12363ff40c12SJohn Marino #ifdef EAP_SERVER_AKA_PRIME
12376d49e1aeSJan Lentfer /**
12386d49e1aeSJan Lentfer  * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
12393ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
12403ff40c12SJohn Marino  * @permanent: Permanent username
12416d49e1aeSJan Lentfer  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
12426d49e1aeSJan Lentfer  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
12436d49e1aeSJan Lentfer  * free it.
12446d49e1aeSJan Lentfer  * @counter: AT_COUNTER value for fast re-authentication
12456d49e1aeSJan Lentfer  * @k_encr: K_encr from the previous full authentication
12466d49e1aeSJan Lentfer  * @k_aut: K_aut from the previous full authentication
12476d49e1aeSJan Lentfer  * @k_re: 32-byte K_re from the previous full authentication
12486d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
12496d49e1aeSJan Lentfer  *
12506d49e1aeSJan Lentfer  * This function adds a new re-authentication entry for an EAP-AKA' user.
12516d49e1aeSJan Lentfer  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
12526d49e1aeSJan Lentfer  * anymore.
12536d49e1aeSJan Lentfer  */
eap_sim_db_add_reauth_prime(struct eap_sim_db_data * data,const char * permanent,char * reauth_id,u16 counter,const u8 * k_encr,const u8 * k_aut,const u8 * k_re)12543ff40c12SJohn Marino int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
12553ff40c12SJohn Marino 				const char *permanent, char *reauth_id,
12563ff40c12SJohn Marino 				u16 counter, const u8 *k_encr,
12573ff40c12SJohn Marino 				const u8 *k_aut, const u8 *k_re)
12586d49e1aeSJan Lentfer {
12596d49e1aeSJan Lentfer 	struct eap_sim_reauth *r;
12606d49e1aeSJan Lentfer 
12613ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
12623ff40c12SJohn Marino 		   "identity '%s'", reauth_id, permanent);
12633ff40c12SJohn Marino 
12643ff40c12SJohn Marino #ifdef CONFIG_SQLITE
12653ff40c12SJohn Marino 	if (data->sqlite_db)
12663ff40c12SJohn Marino 		return db_add_reauth(data, permanent, reauth_id, counter, NULL,
12673ff40c12SJohn Marino 				     k_encr, k_aut, k_re);
12683ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
12693ff40c12SJohn Marino 	r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
12706d49e1aeSJan Lentfer 	if (r == NULL)
12716d49e1aeSJan Lentfer 		return -1;
12726d49e1aeSJan Lentfer 
12736d49e1aeSJan Lentfer 	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
12746d49e1aeSJan Lentfer 	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
12756d49e1aeSJan Lentfer 	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
12766d49e1aeSJan Lentfer 
12776d49e1aeSJan Lentfer 	return 0;
12786d49e1aeSJan Lentfer }
12793ff40c12SJohn Marino #endif /* EAP_SERVER_AKA_PRIME */
12806d49e1aeSJan Lentfer 
12816d49e1aeSJan Lentfer 
12826d49e1aeSJan Lentfer /**
12836d49e1aeSJan Lentfer  * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
12843ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
12853ff40c12SJohn Marino  * @pseudonym: Pseudonym username
12863ff40c12SJohn Marino  * Returns: Pointer to permanent username or %NULL if not found
12876d49e1aeSJan Lentfer  */
12883ff40c12SJohn Marino const char *
eap_sim_db_get_permanent(struct eap_sim_db_data * data,const char * pseudonym)12893ff40c12SJohn Marino eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym)
12906d49e1aeSJan Lentfer {
12916d49e1aeSJan Lentfer 	struct eap_sim_pseudonym *p;
12926d49e1aeSJan Lentfer 
12933ff40c12SJohn Marino #ifdef CONFIG_SQLITE
12943ff40c12SJohn Marino 	if (data->sqlite_db)
12953ff40c12SJohn Marino 		return db_get_pseudonym(data, pseudonym);
12963ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
12976d49e1aeSJan Lentfer 
12983ff40c12SJohn Marino 	p = data->pseudonyms;
12993ff40c12SJohn Marino 	while (p) {
13003ff40c12SJohn Marino 		if (os_strcmp(p->pseudonym, pseudonym) == 0)
13013ff40c12SJohn Marino 			return p->permanent;
13023ff40c12SJohn Marino 		p = p->next;
13033ff40c12SJohn Marino 	}
13046d49e1aeSJan Lentfer 
13053ff40c12SJohn Marino 	return NULL;
13066d49e1aeSJan Lentfer }
13076d49e1aeSJan Lentfer 
13086d49e1aeSJan Lentfer 
13096d49e1aeSJan Lentfer /**
13106d49e1aeSJan Lentfer  * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
13113ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
13123ff40c12SJohn Marino  * @reauth_id: Fast re-authentication username
13136d49e1aeSJan Lentfer  * Returns: Pointer to the re-auth entry, or %NULL if not found
13146d49e1aeSJan Lentfer  */
13156d49e1aeSJan Lentfer struct eap_sim_reauth *
eap_sim_db_get_reauth_entry(struct eap_sim_db_data * data,const char * reauth_id)13163ff40c12SJohn Marino eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
13173ff40c12SJohn Marino 			    const char *reauth_id)
13186d49e1aeSJan Lentfer {
13196d49e1aeSJan Lentfer 	struct eap_sim_reauth *r;
13206d49e1aeSJan Lentfer 
13213ff40c12SJohn Marino #ifdef CONFIG_SQLITE
13223ff40c12SJohn Marino 	if (data->sqlite_db)
13233ff40c12SJohn Marino 		return db_get_reauth(data, reauth_id);
13243ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
13253ff40c12SJohn Marino 
13263ff40c12SJohn Marino 	r = data->reauths;
13273ff40c12SJohn Marino 	while (r) {
13283ff40c12SJohn Marino 		if (os_strcmp(r->reauth_id, reauth_id) == 0)
13293ff40c12SJohn Marino 			break;
13303ff40c12SJohn Marino 		r = r->next;
13313ff40c12SJohn Marino 	}
13323ff40c12SJohn Marino 
13336d49e1aeSJan Lentfer 	return r;
13346d49e1aeSJan Lentfer }
13356d49e1aeSJan Lentfer 
13366d49e1aeSJan Lentfer 
13376d49e1aeSJan Lentfer /**
13386d49e1aeSJan Lentfer  * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
13393ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
13406d49e1aeSJan Lentfer  * @reauth: Pointer to re-authentication entry from
13416d49e1aeSJan Lentfer  * eap_sim_db_get_reauth_entry()
13426d49e1aeSJan Lentfer  */
eap_sim_db_remove_reauth(struct eap_sim_db_data * data,struct eap_sim_reauth * reauth)13433ff40c12SJohn Marino void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
13443ff40c12SJohn Marino 			      struct eap_sim_reauth *reauth)
13456d49e1aeSJan Lentfer {
13466d49e1aeSJan Lentfer 	struct eap_sim_reauth *r, *prev = NULL;
13473ff40c12SJohn Marino #ifdef CONFIG_SQLITE
13483ff40c12SJohn Marino 	if (data->sqlite_db) {
13493ff40c12SJohn Marino 		db_remove_reauth(data, reauth);
13503ff40c12SJohn Marino 		return;
13513ff40c12SJohn Marino 	}
13523ff40c12SJohn Marino #endif /* CONFIG_SQLITE */
13536d49e1aeSJan Lentfer 	r = data->reauths;
13546d49e1aeSJan Lentfer 	while (r) {
13556d49e1aeSJan Lentfer 		if (r == reauth) {
13566d49e1aeSJan Lentfer 			if (prev)
13576d49e1aeSJan Lentfer 				prev->next = r->next;
13586d49e1aeSJan Lentfer 			else
13596d49e1aeSJan Lentfer 				data->reauths = r->next;
13606d49e1aeSJan Lentfer 			eap_sim_db_free_reauth(r);
13616d49e1aeSJan Lentfer 			return;
13626d49e1aeSJan Lentfer 		}
13636d49e1aeSJan Lentfer 		prev = r;
13646d49e1aeSJan Lentfer 		r = r->next;
13656d49e1aeSJan Lentfer 	}
13666d49e1aeSJan Lentfer }
13676d49e1aeSJan Lentfer 
13686d49e1aeSJan Lentfer 
13696d49e1aeSJan Lentfer /**
13706d49e1aeSJan Lentfer  * eap_sim_db_get_aka_auth - Get AKA authentication values
13713ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
13723ff40c12SJohn Marino  * @username: Permanent username (prefix | IMSI)
13736d49e1aeSJan Lentfer  * @_rand: Buffer for RAND value
13746d49e1aeSJan Lentfer  * @autn: Buffer for AUTN value
13756d49e1aeSJan Lentfer  * @ik: Buffer for IK value
13766d49e1aeSJan Lentfer  * @ck: Buffer for CK value
13776d49e1aeSJan Lentfer  * @res: Buffer for RES value
13786d49e1aeSJan Lentfer  * @res_len: Buffer for RES length
13796d49e1aeSJan Lentfer  * @cb_session_ctx: Session callback context for get_complete_cb()
13806d49e1aeSJan Lentfer  * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
13816d49e1aeSJan Lentfer  * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
13826d49e1aeSJan Lentfer  * case, the callback function registered with eap_sim_db_init() will be
13836d49e1aeSJan Lentfer  * called once the results become available.
13846d49e1aeSJan Lentfer  *
13856d49e1aeSJan Lentfer  * When using an external server for AKA authentication, this function can
13866d49e1aeSJan Lentfer  * always start a request and return EAP_SIM_DB_PENDING immediately if
13876d49e1aeSJan Lentfer  * authentication triplets are not available. Once the authentication data are
13886d49e1aeSJan Lentfer  * received, callback function registered with eap_sim_db_init() is called to
13896d49e1aeSJan Lentfer  * notify EAP state machine to reprocess the message. This
13906d49e1aeSJan Lentfer  * eap_sim_db_get_aka_auth() function will then be called again and the newly
13916d49e1aeSJan Lentfer  * received triplets will then be given to the caller.
13926d49e1aeSJan Lentfer  */
eap_sim_db_get_aka_auth(struct eap_sim_db_data * data,const char * username,u8 * _rand,u8 * autn,u8 * ik,u8 * ck,u8 * res,size_t * res_len,void * cb_session_ctx)13933ff40c12SJohn Marino int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
13943ff40c12SJohn Marino 			    u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
13953ff40c12SJohn Marino 			    u8 *res, size_t *res_len, void *cb_session_ctx)
13966d49e1aeSJan Lentfer {
13976d49e1aeSJan Lentfer 	struct eap_sim_db_pending *entry;
13986d49e1aeSJan Lentfer 	int len;
13996d49e1aeSJan Lentfer 	char msg[40];
14003ff40c12SJohn Marino 	const char *imsi;
14013ff40c12SJohn Marino 	size_t imsi_len;
14026d49e1aeSJan Lentfer 
14033ff40c12SJohn Marino 	if (username == NULL ||
14043ff40c12SJohn Marino 	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
14053ff40c12SJohn Marino 	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
14063ff40c12SJohn Marino 	    username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
14073ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
14083ff40c12SJohn Marino 			   username);
14096d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
14106d49e1aeSJan Lentfer 	}
14113ff40c12SJohn Marino 	imsi = username + 1;
14123ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
14133ff40c12SJohn Marino 		   imsi);
14146d49e1aeSJan Lentfer 
14153ff40c12SJohn Marino 	entry = eap_sim_db_get_pending(data, imsi, 1);
14166d49e1aeSJan Lentfer 	if (entry) {
14176d49e1aeSJan Lentfer 		if (entry->state == FAILURE) {
1418*a1157835SDaniel Fojt 			eap_sim_db_free_pending(data, entry);
14196d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
14206d49e1aeSJan Lentfer 			return EAP_SIM_DB_FAILURE;
14216d49e1aeSJan Lentfer 		}
14226d49e1aeSJan Lentfer 
14236d49e1aeSJan Lentfer 		if (entry->state == PENDING) {
14246d49e1aeSJan Lentfer 			eap_sim_db_add_pending(data, entry);
14256d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
14266d49e1aeSJan Lentfer 			return EAP_SIM_DB_PENDING;
14276d49e1aeSJan Lentfer 		}
14286d49e1aeSJan Lentfer 
14296d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
14306d49e1aeSJan Lentfer 			   "received authentication data");
14316d49e1aeSJan Lentfer 		os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
14326d49e1aeSJan Lentfer 		os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
14336d49e1aeSJan Lentfer 		os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
14346d49e1aeSJan Lentfer 		os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
14356d49e1aeSJan Lentfer 		os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
14366d49e1aeSJan Lentfer 		*res_len = entry->u.aka.res_len;
1437*a1157835SDaniel Fojt 		eap_sim_db_free_pending(data, entry);
14386d49e1aeSJan Lentfer 		return 0;
14396d49e1aeSJan Lentfer 	}
14406d49e1aeSJan Lentfer 
14416d49e1aeSJan Lentfer 	if (data->sock < 0) {
14426d49e1aeSJan Lentfer 		if (eap_sim_db_open_socket(data) < 0)
14436d49e1aeSJan Lentfer 			return EAP_SIM_DB_FAILURE;
14446d49e1aeSJan Lentfer 	}
14456d49e1aeSJan Lentfer 
14463ff40c12SJohn Marino 	imsi_len = os_strlen(imsi);
14476d49e1aeSJan Lentfer 	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1448*a1157835SDaniel Fojt 	if (os_snprintf_error(sizeof(msg), len) ||
1449*a1157835SDaniel Fojt 	    len + imsi_len >= sizeof(msg))
14506d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
14513ff40c12SJohn Marino 	os_memcpy(msg + len, imsi, imsi_len);
14523ff40c12SJohn Marino 	len += imsi_len;
14536d49e1aeSJan Lentfer 
14543ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
14553ff40c12SJohn Marino 		    "data for IMSI '%s'", imsi);
14566d49e1aeSJan Lentfer 	if (eap_sim_db_send(data, msg, len) < 0)
14576d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
14586d49e1aeSJan Lentfer 
14596d49e1aeSJan Lentfer 	entry = os_zalloc(sizeof(*entry));
14606d49e1aeSJan Lentfer 	if (entry == NULL)
14616d49e1aeSJan Lentfer 		return EAP_SIM_DB_FAILURE;
14626d49e1aeSJan Lentfer 
14636d49e1aeSJan Lentfer 	entry->aka = 1;
14643ff40c12SJohn Marino 	os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
14656d49e1aeSJan Lentfer 	entry->cb_session_ctx = cb_session_ctx;
14666d49e1aeSJan Lentfer 	entry->state = PENDING;
14676d49e1aeSJan Lentfer 	eap_sim_db_add_pending(data, entry);
1468*a1157835SDaniel Fojt 	eap_sim_db_expire_pending(data, entry);
1469*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
14706d49e1aeSJan Lentfer 
14716d49e1aeSJan Lentfer 	return EAP_SIM_DB_PENDING;
14726d49e1aeSJan Lentfer }
14736d49e1aeSJan Lentfer 
14746d49e1aeSJan Lentfer 
14756d49e1aeSJan Lentfer /**
14766d49e1aeSJan Lentfer  * eap_sim_db_resynchronize - Resynchronize AKA AUTN
14773ff40c12SJohn Marino  * @data: Private data pointer from eap_sim_db_init()
14783ff40c12SJohn Marino  * @username: Permanent username
14796d49e1aeSJan Lentfer  * @auts: AUTS value from the peer
14806d49e1aeSJan Lentfer  * @_rand: RAND value used in the rejected message
14816d49e1aeSJan Lentfer  * Returns: 0 on success, -1 on failure
14826d49e1aeSJan Lentfer  *
14836d49e1aeSJan Lentfer  * This function is called when the peer reports synchronization failure in the
14846d49e1aeSJan Lentfer  * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
14856d49e1aeSJan Lentfer  * HLR/AuC to allow it to resynchronize with the peer. After this,
14866d49e1aeSJan Lentfer  * eap_sim_db_get_aka_auth() will be called again to to fetch updated
14876d49e1aeSJan Lentfer  * RAND/AUTN values for the next challenge.
14886d49e1aeSJan Lentfer  */
eap_sim_db_resynchronize(struct eap_sim_db_data * data,const char * username,const u8 * auts,const u8 * _rand)14893ff40c12SJohn Marino int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
14903ff40c12SJohn Marino 			     const char *username,
14913ff40c12SJohn Marino 			     const u8 *auts, const u8 *_rand)
14926d49e1aeSJan Lentfer {
14933ff40c12SJohn Marino 	const char *imsi;
14943ff40c12SJohn Marino 	size_t imsi_len;
14956d49e1aeSJan Lentfer 
14963ff40c12SJohn Marino 	if (username == NULL ||
14973ff40c12SJohn Marino 	    (username[0] != EAP_AKA_PERMANENT_PREFIX &&
14983ff40c12SJohn Marino 	     username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
14993ff40c12SJohn Marino 	    username[1] == '\0' || os_strlen(username) > 20) {
15003ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
15013ff40c12SJohn Marino 			   username);
15026d49e1aeSJan Lentfer 		return -1;
15036d49e1aeSJan Lentfer 	}
15043ff40c12SJohn Marino 	imsi = username + 1;
15053ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
15063ff40c12SJohn Marino 		   imsi);
15076d49e1aeSJan Lentfer 
15086d49e1aeSJan Lentfer 	if (data->sock >= 0) {
15096d49e1aeSJan Lentfer 		char msg[100];
15106d49e1aeSJan Lentfer 		int len, ret;
15116d49e1aeSJan Lentfer 
15123ff40c12SJohn Marino 		imsi_len = os_strlen(imsi);
15136d49e1aeSJan Lentfer 		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1514*a1157835SDaniel Fojt 		if (os_snprintf_error(sizeof(msg), len) ||
1515*a1157835SDaniel Fojt 		    len + imsi_len >= sizeof(msg))
15166d49e1aeSJan Lentfer 			return -1;
15173ff40c12SJohn Marino 		os_memcpy(msg + len, imsi, imsi_len);
15183ff40c12SJohn Marino 		len += imsi_len;
15196d49e1aeSJan Lentfer 
15206d49e1aeSJan Lentfer 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1521*a1157835SDaniel Fojt 		if (os_snprintf_error(sizeof(msg) - len, ret))
15226d49e1aeSJan Lentfer 			return -1;
15236d49e1aeSJan Lentfer 		len += ret;
15246d49e1aeSJan Lentfer 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
15256d49e1aeSJan Lentfer 					auts, EAP_AKA_AUTS_LEN);
15266d49e1aeSJan Lentfer 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1527*a1157835SDaniel Fojt 		if (os_snprintf_error(sizeof(msg) - len, ret))
15286d49e1aeSJan Lentfer 			return -1;
15296d49e1aeSJan Lentfer 		len += ret;
15306d49e1aeSJan Lentfer 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
15316d49e1aeSJan Lentfer 					_rand, EAP_AKA_RAND_LEN);
15323ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
15333ff40c12SJohn Marino 			   "IMSI '%s'", imsi);
15346d49e1aeSJan Lentfer 		if (eap_sim_db_send(data, msg, len) < 0)
15356d49e1aeSJan Lentfer 			return -1;
15366d49e1aeSJan Lentfer 	}
15376d49e1aeSJan Lentfer 
15386d49e1aeSJan Lentfer 	return 0;
15396d49e1aeSJan Lentfer }
15403ff40c12SJohn Marino 
15413ff40c12SJohn Marino 
15423ff40c12SJohn Marino /**
15433ff40c12SJohn Marino  * sim_get_username - Extract username from SIM identity
15443ff40c12SJohn Marino  * @identity: Identity
15453ff40c12SJohn Marino  * @identity_len: Identity length
15463ff40c12SJohn Marino  * Returns: Allocated buffer with the username part of the identity
15473ff40c12SJohn Marino  *
15483ff40c12SJohn Marino  * Caller is responsible for freeing the returned buffer with os_free().
15493ff40c12SJohn Marino  */
sim_get_username(const u8 * identity,size_t identity_len)15503ff40c12SJohn Marino char * sim_get_username(const u8 *identity, size_t identity_len)
15513ff40c12SJohn Marino {
15523ff40c12SJohn Marino 	size_t pos;
15533ff40c12SJohn Marino 
15543ff40c12SJohn Marino 	if (identity == NULL)
15553ff40c12SJohn Marino 		return NULL;
15563ff40c12SJohn Marino 
15573ff40c12SJohn Marino 	for (pos = 0; pos < identity_len; pos++) {
15583ff40c12SJohn Marino 		if (identity[pos] == '@' || identity[pos] == '\0')
15593ff40c12SJohn Marino 			break;
15603ff40c12SJohn Marino 	}
15613ff40c12SJohn Marino 
15623ff40c12SJohn Marino 	return dup_binstr(identity, pos);
15633ff40c12SJohn Marino }
1564