EAP-SIM DB: Optional use of SQLite database for pseudonyms
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 30 Aug 2012 13:04:52 +0000 (16:04 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 30 Aug 2012 13:04:52 +0000 (16:04 +0300)
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 <jouni@qca.qualcomm.com>

hostapd/Makefile
hostapd/hostapd.conf
src/eap_server/eap_sim_db.c

index 6809b07..f5dfce0 100644 (file)
@@ -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
index 5a2c2ea..edbd772 100644 (file)
@@ -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=<path> 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
index 68fb1f0..a9921ac 100644 (file)
@@ -17,6 +17,9 @@
 
 #include "includes.h"
 #include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#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);