]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hlr_auc_gw: Add SQLite database support for Milenage information
authorJouni Malinen <j@w1.fi>
Sun, 19 Aug 2012 19:59:30 +0000 (22:59 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 19 Aug 2012 19:59:30 +0000 (22:59 +0300)
An SQLite database can now be used to manage the Milenage information
instead of a text file. The new hlr_auc_gw.txt document describes how
this is configured and used.

Signed-hostap: Jouni Malinen <j@w1.fi>

hostapd/Makefile
hostapd/defconfig
hostapd/hlr_auc_gw.c
hostapd/hlr_auc_gw.txt [new file with mode: 0644]

index 7bd080f54a82cedcfe0e3e876b25b005eb491082..420bc5b67e8add601a82c2ca2bea6be6a7b2c2e4 100644 (file)
@@ -884,6 +884,11 @@ 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 3419a18cf1fc7af56d5f39147bdad09220dc7e90..204aa768301f5d0e7cb4d6863c589226c106160a 100644 (file)
@@ -264,3 +264,6 @@ CONFIG_IPV6=y
 
 # Hotspot 2.0
 #CONFIG_HS20=y
+
+# Enable SQLite database support in hlr_auc_gw
+#CONFIG_SQLITE=y
index 02a6a59fd804b74656bde3cfa1a89a209bffec1b..e27ddaba97d7c1d42a33ece842ac8f3f3d591f1b 100644 (file)
@@ -43,6 +43,9 @@
 
 #include "includes.h"
 #include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "crypto/milenage.h"
@@ -89,6 +92,140 @@ static struct milenage_parameters *milenage_db = NULL;
 #define EAP_AKA_CK_LEN 16
 
 
+#ifdef CONFIG_SQLITE
+
+static sqlite3 *sqlite_db = NULL;
+static struct milenage_parameters db_tmp_milenage;
+
+
+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_milenage(sqlite3 *db)
+{
+       char *err = NULL;
+       const char *sql =
+               "CREATE TABLE milenage("
+               "  imsi INTEGER PRIMARY KEY NOT NULL,"
+               "  ki CHAR(32) NOT NULL,"
+               "  opc CHAR(32) NOT NULL,"
+               "  amf CHAR(4) NOT NULL,"
+               "  sqn CHAR(12) NOT NULL"
+               ");";
+
+       printf("Adding database table for milenage information\n");
+       if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+               printf("SQLite error: %s\n", err);
+               sqlite3_free(err);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+       sqlite3 *db;
+
+       if (sqlite3_open(db_file, &db)) {
+               printf("Failed to open database %s: %s\n",
+                      db_file, sqlite3_errmsg(db));
+               sqlite3_close(db);
+               return NULL;
+       }
+
+       if (!db_table_exists(db, "milenage") &&
+           db_table_create_milenage(db) < 0) {
+               sqlite3_close(db);
+               return NULL;
+       }
+
+       return db;
+}
+
+
+static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+       struct milenage_parameters *m = ctx;
+       int i;
+
+       for (i = 0; i < argc; i++) {
+               if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
+                   hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
+                       printf("Invalid ki value in database\n");
+                       return -1;
+               }
+
+               if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
+                   hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
+                       printf("Invalid opcvalue in database\n");
+                       return -1;
+               }
+
+               if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
+                   hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
+                       printf("Invalid amf value in database\n");
+                       return -1;
+               }
+
+               if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
+                   hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
+                       printf("Invalid sqn value in database\n");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
+{
+       char cmd[128];
+       unsigned long long imsi;
+
+       os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
+       imsi = atoll(imsi_txt);
+       os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
+                   "%llu", imsi);
+       os_snprintf(cmd, sizeof(cmd),
+                   "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
+                   imsi);
+       if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
+                        NULL) != SQLITE_OK)
+               return NULL;
+
+       return &db_tmp_milenage;
+}
+
+
+static int db_update_milenage_sqn(struct milenage_parameters *m)
+{
+       char cmd[128], val[13], *pos;
+
+       pos = val;
+       pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
+       *pos = '\0';
+       os_snprintf(cmd, sizeof(cmd),
+                   "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
+                   val, m->imsi);
+       if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+               printf("Failed to update SQN in database for IMSI %s\n",
+                      m->imsi);
+               return -1;
+       }
+       return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
 static int open_socket(const char *path)
 {
        struct sockaddr_un addr;
@@ -460,6 +597,11 @@ static struct milenage_parameters * get_milenage(const char *imsi)
                m = m->next;
        }
 
+#ifdef CONFIG_SQLITE
+       if (!m)
+               m = db_get_milenage(imsi);
+#endif /* CONFIG_SQLITE */
+
        return m;
 }
 
@@ -585,6 +727,9 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
                        return;
                res_len = EAP_AKA_RES_MAX_LEN;
                inc_sqn(m->sqn);
+#ifdef CONFIG_SQLITE
+               db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
                sqn_changes = 1;
                printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
                       m->sqn[0], m->sqn[1], m->sqn[2],
@@ -677,6 +822,9 @@ static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
                printf("AKA-AUTS: Re-synchronized: "
                       "SQN=%02x%02x%02x%02x%02x%02x\n",
                       sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+#ifdef CONFIG_SQLITE
+               db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
                sqn_changes = 1;
        }
 }
@@ -743,6 +891,13 @@ static void cleanup(void)
 
        close(serv_sock);
        unlink(socket_path);
+
+#ifdef CONFIG_SQLITE
+       if (sqlite_db) {
+               sqlite3_close(sqlite_db);
+               sqlite_db = NULL;
+       }
+#endif /* CONFIG_SQLITE */
 }
 
 
@@ -762,7 +917,7 @@ static void usage(void)
               "usage:\n"
               "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
               "[-m<milenage file>] \\\n"
-              "        [-i<IND len in bits>]\n"
+              "        [-D<DB file>] [-i<IND len in bits>]\n"
               "\n"
               "options:\n"
               "  -h = show this usage help\n"
@@ -771,6 +926,7 @@ static void usage(void)
               "                    (default: %s)\n"
               "  -g<triplet file> = path for GSM authentication triplets\n"
               "  -m<milenage file> = path for Milenage keys\n"
+              "  -D<DB file> = path to SQLite database\n"
               "  -i<IND len in bits> = IND length for SQN (default: 5)\n",
               default_socket_path);
 }
@@ -780,6 +936,7 @@ int main(int argc, char *argv[])
 {
        int c;
        char *gsm_triplet_file = NULL;
+       char *sqlite_db_file = NULL;
 
        if (os_program_init())
                return -1;
@@ -787,10 +944,18 @@ int main(int argc, char *argv[])
        socket_path = default_socket_path;
 
        for (;;) {
-               c = getopt(argc, argv, "g:hi:m:s:u");
+               c = getopt(argc, argv, "D:g:hi:m:s:u");
                if (c < 0)
                        break;
                switch (c) {
+               case 'D':
+#ifdef CONFIG_SQLITE
+                       sqlite_db_file = optarg;
+                       break;
+#else /* CONFIG_SQLITE */
+                       printf("No SQLite support included in the build\n");
+                       return -1;
+#endif /* CONFIG_SQLITE */
                case 'g':
                        gsm_triplet_file = optarg;
                        break;
@@ -819,6 +984,16 @@ int main(int argc, char *argv[])
                }
        }
 
+       if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
+               usage();
+               return -1;
+       }
+
+#ifdef CONFIG_SQLITE
+       if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
+               return -1;
+#endif /* CONFIG_SQLITE */
+
        if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
                return -1;
 
@@ -838,6 +1013,13 @@ int main(int argc, char *argv[])
        for (;;)
                process(serv_sock);
 
+#ifdef CONFIG_SQLITE
+       if (sqlite_db) {
+               sqlite3_close(sqlite_db);
+               sqlite_db = NULL;
+       }
+#endif /* CONFIG_SQLITE */
+
        os_program_deinit();
 
        return 0;
diff --git a/hostapd/hlr_auc_gw.txt b/hostapd/hlr_auc_gw.txt
new file mode 100644 (file)
index 0000000..e4b6783
--- /dev/null
@@ -0,0 +1,98 @@
+HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+
+hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
+database/authentication gateway interface to HLR/AuC. It could be
+replaced with an implementation of SS7 gateway to GSM/UMTS
+authentication center (HLR/AuC). hostapd will send SIM/AKA
+authentication queries over a UNIX domain socket to and external
+program, e.g., hlr_auc_gw.
+
+hlr_auc_gw can be configured with GSM and UMTS authentication data with
+text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
+hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
+dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
+GSM triplet data is used for a more static configuration (e.g., triplets
+extracted from a SIM card).
+
+Alternatively, hlr_auc_gw can be built with support for an SQLite
+database for more dynamic operations. This is enabled by adding
+"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
+clean; make hlr_auc_gw" in this directory).
+
+hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
+hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
+is configured with command line parameters:
+
+hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
+        [-D<DB file>] [-i<IND len in bits>]
+
+options:
+  -h = show this usage help
+  -u = update SQN in Milenage file on exit
+  -s<socket path> = path for UNIX domain socket
+                    (default: /tmp/hlr_auc_gw.sock)
+  -g<triplet file> = path for GSM authentication triplets
+  -m<milenage file> = path for Milenage keys
+  -D<DB file> = path to SQLite database
+  -i<IND len in bits> = IND length for SQN (default: 5)
+
+
+The SQLite database can be initialized with sqlite, e.g., by running
+following commands in "sqlite3 /path/to/hlr_auc_gw.db":
+
+CREATE TABLE milenage(
+       imsi INTEGER PRIMARY KEY NOT NULL,
+       ki CHAR(32) NOT NULL,
+       opc CHAR(32) NOT NULL,
+       amf CHAR(4) NOT NULL,
+       sqn CHAR(12) NOT NULL
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+       232010000000000,
+       '90dca4eda45b53cf0f12d7c9c3bc6a89',
+       'cb9cccc4b9258e6dca4760379fb82581',
+       '61df',
+       '000000000000'
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+       555444333222111,
+       '5122250214c33e723a5dd523fc145fc0',
+       '981d464c7c52eb6e5036234984ad0bcf',
+       'c3ab',
+       '16f3b3f70fc1'
+);
+
+
+"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
+Milenage parameters based on IMSI from the database. The database can be
+updated dynamically while hlr_auc_gw is running to add/remove/modify
+entries.
+
+
+Example configuration files for hostapd to operate as a RADIUS
+authentication server for EAP-SIM/AKA/AKA':
+
+hostapd.conf:
+
+driver=none
+radius_server_clients=hostapd.radius_clients
+eap_server=1
+eap_user_file=hostapd.eap_user
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+eap_sim_aka_result_ind=1
+
+hostapd.radius_clients:
+
+0.0.0.0/0      radius
+
+hostapd.eap_user:
+
+"0"*   AKA
+"1"*   SIM
+"2"*   AKA
+"3"*   SIM
+"4"*   AKA
+"5"*   SIM
+"6"*   AKA'
+"7"*   AKA'
+"8"*   AKA'