hlr_auc_gw: Add SQLite database support for Milenage information
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>
This commit is contained in:
parent
e43bd7a1b7
commit
597d5d1b11
4 changed files with 290 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -264,3 +264,6 @@ CONFIG_IPV6=y
|
|||
|
||||
# Hotspot 2.0
|
||||
#CONFIG_HS20=y
|
||||
|
||||
# Enable SQLite database support in hlr_auc_gw
|
||||
#CONFIG_SQLITE=y
|
||||
|
|
|
@ -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;
|
||||
|
|
98
hostapd/hlr_auc_gw.txt
Normal file
98
hostapd/hlr_auc_gw.txt
Normal file
|
@ -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'
|
Loading…
Reference in a new issue