From 66979bb833047cce3d935e24072b1af4b356e6fe Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 30 Aug 2012 16:04:52 +0300 Subject: [PATCH] EAP-SIM DB: Optional use of SQLite database for pseudonyms This allows hostapd to use an SQLite database for storing EAP-SIM/AKA pseudonyms over process restarts. CONFIG_SQLITE=y build option adds support for this and the SQLite database file is specified in eap_sib_db configuration parameter. Signed-hostap: Jouni Malinen --- hostapd/Makefile | 11 +- hostapd/hostapd.conf | 4 +- src/eap_server/eap_sim_db.c | 220 ++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 6 deletions(-) diff --git a/hostapd/Makefile b/hostapd/Makefile index 6809b0766..f5dfce0b2 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -825,6 +825,12 @@ ifdef CONFIG_DEBUG_FILE CFLAGS += -DCONFIG_DEBUG_FILE endif +ifdef CONFIG_SQLITE +CFLAGS += -DCONFIG_SQLITE +LIBS += -lsqlite3 +LIBS_h += -lsqlite3 +endif + ALL=hostapd hostapd_cli all: verify_config $(ALL) @@ -892,11 +898,6 @@ ifdef TLS_FUNCS LIBS_n += -lcrypto endif -ifdef CONFIG_SQLITE -CFLAGS += -DCONFIG_SQLITE -LIBS_h += -lsqlite3 -endif - HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o HOBJS += ../src/crypto/aes-encblock.o ifdef CONFIG_INTERNAL_AES diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 5a2c2ea73..edbd77234 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -681,8 +681,10 @@ eap_server=0 # This is a text string in implementation specific format. The example # implementation in eap_sim_db.c uses this as the UNIX domain socket name for # the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:" -# prefix. +# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config), +# database file can be described with an optional db= parameter. #eap_sim_db=unix:/tmp/hlr_auc_gw.sock +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, # random value. It is configured as a 16-octet value in hex format. It can be diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index 68fb1f0a6..a9921acec 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -17,6 +17,9 @@ #include "includes.h" #include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "common.h" #include "crypto/random.h" @@ -66,9 +69,190 @@ struct eap_sim_db_data { struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; +#ifdef CONFIG_SQLITE + sqlite3 *sqlite_db; + u8 db_tmp_identity[100]; + char db_tmp_pseudonym_str[100]; + struct eap_sim_pseudonym db_tmp_pseudonym; +#endif /* CONFIG_SQLITE */ }; +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_pseudonym(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE pseudonyms(" + " imsi INTEGER PRIMARY KEY NOT NULL," + " pseudonym CHAR(21) NOT NULL" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "pseudonym information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " + "%s: %s", db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "pseudonyms") && + db_table_create_pseudonym(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int valid_pseudonym_string(const char *pseudonym) +{ + const char *pos = pseudonym; + while (*pos) { + if ((*pos < '0' || *pos > '9') && + (*pos < 'a' || *pos > 'f')) + return 0; + pos++; + } + return 1; +} + + +static int db_add_pseudonym(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len, char *pseudonym) +{ + char cmd[128]; + unsigned long long imsi; + + if (!valid_pseudonym_string(pseudonym) || identity_len >= sizeof(cmd)) + { + os_free(pseudonym); + return -1; + } + os_memcpy(cmd, identity, identity_len); + cmd[identity_len] = '\0'; + imsi = atoll(cmd); + + os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " + "(imsi, pseudonym) VALUES (%llu , '%s');", + imsi, pseudonym); + os_free(pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, NULL, data, NULL) != SQLITE_OK) + return -1; + + return 0; +} + + +static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "imsi") == 0 && argv[i]) { + len = os_strlen(argv[i]); + if (len > sizeof(data->db_tmp_identity)) + continue; + os_memcpy(data->db_tmp_identity, argv[i], len); + data->db_tmp_pseudonym.identity = + data->db_tmp_identity; + data->db_tmp_pseudonym.identity_len = len; + } else if (os_strcmp(col[i], "pseudonym") == 0 && argv[i]) { + len = os_strlen(argv[i]); + if (len >= sizeof(data->db_tmp_pseudonym_str)) + continue; + os_memcpy(data->db_tmp_pseudonym_str, argv[i], len); + data->db_tmp_pseudonym_str[len] = '\0'; + data->db_tmp_pseudonym.pseudonym = + data->db_tmp_pseudonym_str; + } + } + + return 0; +} + + +static struct eap_sim_pseudonym * +db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) +{ + char cmd[128]; + + if (!valid_pseudonym_string(pseudonym)) + return NULL; + os_memset(&data->db_tmp_pseudonym, 0, sizeof(data->db_tmp_pseudonym)); + os_strlcpy(data->db_tmp_pseudonym_str, pseudonym, + sizeof(data->db_tmp_pseudonym_str)); + data->db_tmp_pseudonym.pseudonym = data->db_tmp_pseudonym_str; + os_snprintf(cmd, sizeof(cmd), + "SELECT imsi FROM pseudonyms WHERE pseudonym='%s';", + pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_pseudonym.identity == NULL) + return NULL; + return &data->db_tmp_pseudonym; +} + + +static struct eap_sim_pseudonym * +db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len) +{ + char cmd[128]; + unsigned long long imsi; + + if (identity_len >= sizeof(cmd)) + return NULL; + os_memcpy(cmd, identity, identity_len); + cmd[identity_len] = '\0'; + imsi = atoll(cmd); + + os_memset(&data->db_tmp_pseudonym, 0, sizeof(data->db_tmp_pseudonym)); + if (identity_len > sizeof(data->db_tmp_identity)) + return NULL; + os_memcpy(data->db_tmp_identity, identity, identity_len); + data->db_tmp_pseudonym.identity = data->db_tmp_identity; + data->db_tmp_pseudonym.identity_len = identity_len; + os_snprintf(cmd, sizeof(cmd), + "SELECT pseudonym FROM pseudonyms WHERE imsi=%llu;", imsi); + if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_pseudonym.pseudonym == NULL) + return NULL; + return &data->db_tmp_pseudonym; +} + +#endif /* CONFIG_SQLITE */ + static struct eap_sim_db_pending * eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, size_t imsi_len, int aka) @@ -395,6 +579,7 @@ void * eap_sim_db_init(const char *config, void *ctx) { struct eap_sim_db_data *data; + char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -406,6 +591,16 @@ void * eap_sim_db_init(const char *config, data->fname = os_strdup(config); if (data->fname == NULL) goto fail; + pos = os_strstr(data->fname, " db="); + if (pos) { + *pos = '\0'; +#ifdef CONFIG_SQLITE + pos += 4; + data->sqlite_db = db_open(pos); + if (data->sqlite_db == NULL) + goto fail; +#endif /* CONFIG_SQLITE */ + } if (os_strncmp(data->fname, "unix:", 5) == 0) { if (eap_sim_db_open_socket(data)) { @@ -452,6 +647,13 @@ void eap_sim_db_deinit(void *priv) struct eap_sim_reauth *r, *prevr; struct eap_sim_db_pending *pending, *prev_pending; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + sqlite3_close(data->sqlite_db); + data->sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + eap_sim_db_close_socket(data); os_free(data->fname); @@ -669,6 +871,14 @@ eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, os_memcpy(pseudonym, identity, len); pseudonym[len] = '\0'; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + p = db_get_pseudonym(data, pseudonym); + os_free(pseudonym); + return p; + } +#endif /* CONFIG_SQLITE */ + p = data->pseudonyms; while (p) { if (os_strcmp(p->pseudonym, pseudonym) == 0) @@ -694,6 +904,11 @@ eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) return NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_pseudonym_id(data, identity, identity_len); +#endif /* CONFIG_SQLITE */ + p = data->pseudonyms; while (p) { if (identity_len == p->identity_len && @@ -939,6 +1154,11 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); /* TODO: could store last two pseudonyms */ +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_pseudonym(data, identity, identity_len, + pseudonym); +#endif /* CONFIG_SQLITE */ p = eap_sim_db_get_pseudonym(data, identity, identity_len); if (p == NULL) p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);