HS 2.0 server: RADIUS server support for SIM provisioning
This adds support for hostapd-as-RADIUS-authentication-server to request subscription remediation for SIM-based credentials. The new hostapd.conf parameter hs20_sim_provisioning_url is used to set the URL prefix for the remediation server for SIM provisioning. The random hotspot2dot0-mobile-identifier-hash value will be added to the end of this URL prefix and the same value is stored in a new SQLite database table sim_provisioning for the subscription server implementation to use. Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
79fec6a92d
commit
7bd8c76a4f
8 changed files with 212 additions and 0 deletions
|
@ -3817,6 +3817,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
|||
} else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
|
||||
os_free(bss->t_c_server_url);
|
||||
bss->t_c_server_url = os_strdup(pos);
|
||||
} else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
|
||||
os_free(bss->hs20_sim_provisioning_url);
|
||||
bss->hs20_sim_provisioning_url = os_strdup(pos);
|
||||
#endif /* CONFIG_HS20 */
|
||||
#ifdef CONFIG_MBO
|
||||
} else if (os_strcmp(buf, "mbo") == 0) {
|
||||
|
|
|
@ -93,3 +93,11 @@ CREATE TABLE cert_enroll(
|
|||
realm TEXT,
|
||||
serialnum TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE sim_provisioning(
|
||||
mobile_identifier_hash TEXT PRIMARY KEY,
|
||||
imsi TEXT,
|
||||
mac_addr TEXT,
|
||||
eap_method TEXT,
|
||||
timestamp TEXT
|
||||
);
|
||||
|
|
|
@ -649,6 +649,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
|
|||
os_free(conf->hs20_operator_icon);
|
||||
}
|
||||
os_free(conf->subscr_remediation_url);
|
||||
os_free(conf->hs20_sim_provisioning_url);
|
||||
os_free(conf->t_c_filename);
|
||||
os_free(conf->t_c_server_url);
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
|
|
@ -597,6 +597,7 @@ struct hostapd_bss_config {
|
|||
unsigned int hs20_deauth_req_timeout;
|
||||
char *subscr_remediation_url;
|
||||
u8 subscr_remediation_method;
|
||||
char *hs20_sim_provisioning_url;
|
||||
char *t_c_filename;
|
||||
u32 t_c_timestamp;
|
||||
char *t_c_server_url;
|
||||
|
|
|
@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
|
|||
#ifdef CONFIG_HS20
|
||||
srv.subscr_remediation_url = conf->subscr_remediation_url;
|
||||
srv.subscr_remediation_method = conf->subscr_remediation_method;
|
||||
srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
|
||||
srv.t_c_server_url = conf->t_c_server_url;
|
||||
#endif /* CONFIG_HS20 */
|
||||
srv.erp = conf->eap_server_erp;
|
||||
|
|
|
@ -174,6 +174,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
|
|||
if (hapd->tmp_eap_user.identity == NULL)
|
||||
return NULL;
|
||||
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
|
||||
hapd->tmp_eap_user.identity_len = identity_len;
|
||||
|
||||
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
|
||||
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
|
||||
|
|
|
@ -357,6 +357,7 @@ struct radius_server_data {
|
|||
|
||||
char *subscr_remediation_url;
|
||||
u8 subscr_remediation_method;
|
||||
char *hs20_sim_provisioning_url;
|
||||
|
||||
char *t_c_server_url;
|
||||
|
||||
|
@ -380,6 +381,44 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
|
|||
static void radius_server_session_remove_timeout(void *eloop_ctx,
|
||||
void *timeout_ctx);
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
#ifdef CONFIG_HS20
|
||||
|
||||
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_sim_provisioning(sqlite3 *db)
|
||||
{
|
||||
char *err = NULL;
|
||||
const char *sql =
|
||||
"CREATE TABLE sim_provisioning("
|
||||
" mobile_identifier_hash TEXT PRIMARY KEY,"
|
||||
" imsi TEXT,"
|
||||
" mac_addr TEXT,"
|
||||
" eap_method TEXT,"
|
||||
" timestamp TEXT"
|
||||
");";
|
||||
|
||||
RADIUS_DEBUG("Adding database table for SIM provisioning information");
|
||||
if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
|
||||
RADIUS_ERROR("SQLite error: %s", err);
|
||||
sqlite3_free(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HS20 */
|
||||
#endif /* CONFIG_SQLITE */
|
||||
|
||||
|
||||
void srv_log(struct radius_session *sess, const char *fmt, ...)
|
||||
PRINTF_FORMAT(2, 3);
|
||||
|
||||
|
@ -866,6 +905,117 @@ static void db_update_last_msk(struct radius_session *sess, const char *msk)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_HS20
|
||||
|
||||
static int radius_server_is_sim_method(struct radius_session *sess)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = eap_get_method(sess->eap);
|
||||
return name &&
|
||||
(os_strcmp(name, "SIM") == 0 ||
|
||||
os_strcmp(name, "AKA") == 0 ||
|
||||
os_strcmp(name, "AKA'") == 0);
|
||||
}
|
||||
|
||||
|
||||
static int radius_server_hs20_missing_sim_pps(struct radius_msg *request)
|
||||
{
|
||||
u8 *buf, *pos, *end, type, sublen;
|
||||
size_t len;
|
||||
|
||||
buf = NULL;
|
||||
for (;;) {
|
||||
if (radius_msg_get_attr_ptr(request,
|
||||
RADIUS_ATTR_VENDOR_SPECIFIC,
|
||||
&buf, &len, buf) < 0)
|
||||
return 0;
|
||||
if (len < 6)
|
||||
continue;
|
||||
pos = buf;
|
||||
end = buf + len;
|
||||
if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
|
||||
continue;
|
||||
pos += 4;
|
||||
|
||||
type = *pos++;
|
||||
sublen = *pos++;
|
||||
if (sublen < 2)
|
||||
continue; /* invalid length */
|
||||
sublen -= 2; /* skip header */
|
||||
if (pos + sublen > end)
|
||||
continue; /* invalid WFA VSA */
|
||||
|
||||
if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION)
|
||||
continue;
|
||||
|
||||
RADIUS_DUMP("HS2.0 mobile device version", pos, sublen);
|
||||
if (sublen < 1 + 2)
|
||||
continue;
|
||||
if (pos[0] == 0)
|
||||
continue; /* Release 1 STA does not support provisioning
|
||||
|
||||
*/
|
||||
/* UpdateIdentifier 0 indicates no PPS MO */
|
||||
return WPA_GET_BE16(pos + 1) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define HS20_MOBILE_ID_HASH_LEN 16
|
||||
|
||||
static int radius_server_sim_provisioning_session(struct radius_session *sess,
|
||||
const u8 *hash)
|
||||
{
|
||||
#ifdef CONFIG_SQLITE
|
||||
char *sql;
|
||||
char addr_txt[ETH_ALEN * 3];
|
||||
char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1];
|
||||
struct os_time now;
|
||||
int res;
|
||||
const char *imsi, *eap_method;
|
||||
|
||||
if (!sess->server->db ||
|
||||
(!db_table_exists(sess->server->db, "sim_provisioning") &&
|
||||
db_table_create_sim_provisioning(sess->server->db) < 0))
|
||||
return -1;
|
||||
|
||||
imsi = eap_get_imsi(sess->eap);
|
||||
if (!imsi)
|
||||
return -1;
|
||||
|
||||
eap_method = eap_get_method(sess->eap);
|
||||
if (!eap_method)
|
||||
return -1;
|
||||
|
||||
os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
|
||||
MAC2STR(sess->mac_addr));
|
||||
wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash,
|
||||
HS20_MOBILE_ID_HASH_LEN);
|
||||
|
||||
os_get_time(&now);
|
||||
sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)",
|
||||
hash_txt, imsi, addr_txt, eap_method, now.sec);
|
||||
if (!sql)
|
||||
return -1;
|
||||
|
||||
if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
|
||||
SQLITE_OK) {
|
||||
RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s",
|
||||
sqlite3_errmsg(sess->server->db));
|
||||
res = -1;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
return res;
|
||||
#endif /* CONFIG_SQLITE */
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
|
||||
static struct radius_msg *
|
||||
radius_server_encapsulate_eap(struct radius_server_data *data,
|
||||
struct radius_client *client,
|
||||
|
@ -979,6 +1129,48 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
|
|||
buf, 0)) {
|
||||
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
|
||||
}
|
||||
} else if (code == RADIUS_CODE_ACCESS_ACCEPT &&
|
||||
data->hs20_sim_provisioning_url &&
|
||||
radius_server_is_sim_method(sess) &&
|
||||
radius_server_hs20_missing_sim_pps(request)) {
|
||||
u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN];
|
||||
size_t prefix_len, url_len;
|
||||
|
||||
RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning");
|
||||
|
||||
if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) {
|
||||
radius_msg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash",
|
||||
hash, HS20_MOBILE_ID_HASH_LEN);
|
||||
|
||||
if (radius_server_sim_provisioning_session(sess, hash) < 0) {
|
||||
radius_msg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prefix_len = os_strlen(data->hs20_sim_provisioning_url);
|
||||
url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN;
|
||||
buf = os_malloc(1 + url_len + 1);
|
||||
if (!buf) {
|
||||
radius_msg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
pos = buf;
|
||||
*pos++ = data->subscr_remediation_method;
|
||||
os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len);
|
||||
pos += prefix_len;
|
||||
wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1,
|
||||
hash, HS20_MOBILE_ID_HASH_LEN);
|
||||
RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s",
|
||||
(char *) &buf[1]);
|
||||
if (!radius_msg_add_wfa(
|
||||
msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
|
||||
buf, 1 + url_len)) {
|
||||
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
|
||||
}
|
||||
os_free(buf);
|
||||
}
|
||||
|
||||
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
|
||||
|
@ -2173,6 +2365,9 @@ radius_server_init(struct radius_server_conf *conf)
|
|||
os_strdup(conf->subscr_remediation_url);
|
||||
}
|
||||
data->subscr_remediation_method = conf->subscr_remediation_method;
|
||||
if (conf->hs20_sim_provisioning_url)
|
||||
data->hs20_sim_provisioning_url =
|
||||
os_strdup(conf->hs20_sim_provisioning_url);
|
||||
|
||||
if (conf->t_c_server_url)
|
||||
data->t_c_server_url = os_strdup(conf->t_c_server_url);
|
||||
|
@ -2293,6 +2488,7 @@ void radius_server_deinit(struct radius_server_data *data)
|
|||
os_free(data->dump_msk_file);
|
||||
#endif /* CONFIG_RADIUS_TEST */
|
||||
os_free(data->subscr_remediation_url);
|
||||
os_free(data->hs20_sim_provisioning_url);
|
||||
os_free(data->t_c_server_url);
|
||||
|
||||
#ifdef CONFIG_SQLITE
|
||||
|
|
|
@ -233,6 +233,7 @@ struct radius_server_conf {
|
|||
|
||||
char *subscr_remediation_url;
|
||||
u8 subscr_remediation_method;
|
||||
char *hs20_sim_provisioning_url;
|
||||
|
||||
char *t_c_server_url;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue