HS 2.0R2: RADIUS server support to request Subscr Remediation

The new hostapd.conf parameter subscr_remediation_url can be used to
define the URL of the Subscription Remediation Server that will be added
in a WFA VSA to Access-Accept message if the SQLite user database
indicates that the user need subscription remediation.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2012-11-21 17:04:21 +02:00 committed by Jouni Malinen
parent ae6d15c722
commit 8d2a9921af
13 changed files with 98 additions and 14 deletions

View File

@ -3075,6 +3075,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "osu_service_desc") == 0) { } else if (os_strcmp(buf, "osu_service_desc") == 0) {
if (hs20_parse_osu_service_desc(bss, pos, line) < 0) if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
errors++; errors++;
} else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
os_free(bss->subscr_remediation_url);
bss->subscr_remediation_url = os_strdup(pos);
} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
bss->subscr_remediation_method = atoi(pos);
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
#ifdef CONFIG_TESTING_OPTIONS #ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \ #define PARSE_TEST_PROBABILITY(_val) \

View File

@ -2,6 +2,7 @@ CREATE TABLE users(
identity TEXT PRIMARY KEY, identity TEXT PRIMARY KEY,
methods TEXT, methods TEXT,
password TEXT, password TEXT,
remediation TEXT,
phase2 INTEGER phase2 INTEGER
); );

View File

@ -546,6 +546,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
} }
os_free(conf->hs20_osu_providers); os_free(conf->hs20_osu_providers);
} }
os_free(conf->subscr_remediation_url);
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements); wpabuf_free(conf->vendor_elements);

View File

@ -126,6 +126,7 @@ struct hostapd_eap_user {
unsigned int wildcard_prefix:1; unsigned int wildcard_prefix:1;
unsigned int password_hash:1; /* whether password is hashed with unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */ * nt_password_hash() */
unsigned int remediation:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
}; };
@ -489,6 +490,8 @@ struct hostapd_bss_config {
} *hs20_osu_providers, *last_osu; } *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count; size_t hs20_osu_providers_count;
unsigned int hs20_deauth_req_timeout; unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */

View File

@ -80,6 +80,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
} }
user->force_version = eap_user->force_version; user->force_version = eap_user->force_version;
user->ttls_auth = eap_user->ttls_auth; user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation;
return 0; return 0;
} }
@ -116,6 +117,10 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
#ifdef CONFIG_RADIUS_TEST #ifdef CONFIG_RADIUS_TEST
srv.dump_msk_file = conf->dump_msk_file; srv.dump_msk_file = conf->dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
#ifdef CONFIG_HS20
srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method;
#endif /* CONFIG_HS20 */
hapd->radius_srv = radius_server_init(&srv); hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) { if (hapd->radius_srv == NULL) {

View File

@ -89,6 +89,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
user->next = (void *) 1; user->next = (void *) 1;
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
set_user_methods(user, argv[i]); set_user_methods(user, argv[i]);
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
user->remediation = strlen(argv[i]) > 0;
} }
} }
@ -173,8 +175,8 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
} }
os_snprintf(cmd, sizeof(cmd), os_snprintf(cmd, sizeof(cmd),
"SELECT password,methods FROM users WHERE " "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
"identity='%s' AND phase2=%d;", id_str, phase2); id_str, phase2);
wpa_printf(MSG_DEBUG, "DB: %s", cmd); wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
SQLITE_OK) { SQLITE_OK) {

View File

@ -35,7 +35,8 @@
static void ieee802_1x_finished(struct hostapd_data *hapd, static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success); struct sta_info *sta, int success,
int remediation);
static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@ -1746,14 +1747,14 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
int preauth) int preauth, int remediation)
{ {
struct hostapd_data *hapd = ctx; struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx; struct sta_info *sta = sta_ctx;
if (preauth) if (preauth)
rsn_preauth_finished(hapd, sta, success); rsn_preauth_finished(hapd, sta, success);
else else
ieee802_1x_finished(hapd, sta, success); ieee802_1x_finished(hapd, sta, success, remediation);
} }
@ -1787,6 +1788,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
} }
user->force_version = eap_user->force_version; user->force_version = eap_user->force_version;
user->ttls_auth = eap_user->ttls_auth; user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation;
return 0; return 0;
} }
@ -2290,7 +2292,8 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
static void ieee802_1x_finished(struct hostapd_data *hapd, static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success) struct sta_info *sta, int success,
int remediation)
{ {
const u8 *key; const u8 *key;
size_t len; size_t len;
@ -2298,6 +2301,14 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKLifetime = 43200;
#ifdef CONFIG_HS20 #ifdef CONFIG_HS20
if (remediation && !sta->remediation) {
sta->remediation = 1;
os_free(sta->remediation_url);
sta->remediation_url =
os_strdup(hapd->conf->subscr_remediation_url);
sta->remediation_method = 1; /* SOAP-XML SPP */
}
if (success) { if (success) {
if (sta->remediation) { if (sta->remediation) {
wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "

View File

@ -32,6 +32,7 @@ struct eap_user {
* nt_password_hash() */ * nt_password_hash() */
int phase2; int phase2;
int force_version; int force_version;
unsigned int remediation:1;
int ttls_auth; /* bitfield of int ttls_auth; /* bitfield of
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
}; };

View File

@ -219,7 +219,8 @@ SM_STATE(AUTH_PAE, DISCONNECTED)
sm->eapolLogoff = FALSE; sm->eapolLogoff = FALSE;
if (!from_initialize) { if (!from_initialize) {
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
sm->flags & EAPOL_SM_PREAUTH); sm->flags & EAPOL_SM_PREAUTH,
sm->remediation);
} }
} }
@ -276,7 +277,7 @@ SM_STATE(AUTH_PAE, HELD)
eap_server_get_name(0, sm->eap_type_supp)); eap_server_get_name(0, sm->eap_type_supp));
} }
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
sm->flags & EAPOL_SM_PREAUTH); sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
} }
@ -302,7 +303,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED)
eap_server_get_name(0, sm->eap_type_authsrv), eap_server_get_name(0, sm->eap_type_authsrv),
extra); extra);
sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
sm->flags & EAPOL_SM_PREAUTH); sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
} }
@ -1001,8 +1002,13 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
struct eap_user *user) struct eap_user *user)
{ {
struct eapol_state_machine *sm = ctx; struct eapol_state_machine *sm = ctx;
return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, int ret;
ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
identity_len, phase2, user); identity_len, phase2, user);
if (user->remediation)
sm->remediation = 1;
return ret;
} }

View File

@ -60,7 +60,8 @@ struct eapol_auth_cb {
size_t datalen); size_t datalen);
void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
size_t datalen); size_t datalen);
void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); void (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
int remediation);
int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
int phase2, struct eap_user *user); int phase2, struct eap_user *user);
int (*sta_entry_alive)(void *ctx, const u8 *addr); int (*sta_entry_alive)(void *ctx, const u8 *addr);

View File

@ -173,6 +173,8 @@ struct eapol_state_machine {
struct eapol_authenticator *eapol; struct eapol_authenticator *eapol;
void *sta; /* station context pointer to use in callbacks */ void *sta; /* station context pointer to use in callbacks */
int remediation;
}; };
#endif /* EAPOL_AUTH_SM_I_H */ #endif /* EAPOL_AUTH_SM_I_H */

View File

@ -77,6 +77,8 @@ struct radius_session {
u8 last_identifier; u8 last_identifier;
struct radius_msg *last_reply; struct radius_msg *last_reply;
u8 last_authenticator[16]; u8 last_authenticator[16];
unsigned int remediation:1;
}; };
/** /**
@ -307,6 +309,9 @@ struct radius_server_data {
#ifdef CONFIG_RADIUS_TEST #ifdef CONFIG_RADIUS_TEST
char *dump_msk_file; char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
char *subscr_remediation_url;
u8 subscr_remediation_method;
}; };
@ -622,6 +627,34 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
} }
} }
#ifdef CONFIG_HS20
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
data->subscr_remediation_url) {
u8 *buf;
size_t url_len = os_strlen(data->subscr_remediation_url);
buf = os_malloc(1 + url_len);
if (buf == NULL) {
radius_msg_free(msg);
return NULL;
}
buf[0] = data->subscr_remediation_method;
os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
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);
} else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
u8 buf[1];
if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
buf, 0)) {
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
}
}
#endif /* CONFIG_HS20 */
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
radius_msg_free(msg); radius_msg_free(msg);
@ -1444,6 +1477,11 @@ radius_server_init(struct radius_server_conf *conf)
} }
} }
if (conf->subscr_remediation_url) {
data->subscr_remediation_url =
os_strdup(conf->subscr_remediation_url);
}
#ifdef CONFIG_RADIUS_TEST #ifdef CONFIG_RADIUS_TEST
if (conf->dump_msk_file) if (conf->dump_msk_file)
data->dump_msk_file = os_strdup(conf->dump_msk_file); data->dump_msk_file = os_strdup(conf->dump_msk_file);
@ -1530,6 +1568,7 @@ void radius_server_deinit(struct radius_server_data *data)
#ifdef CONFIG_RADIUS_TEST #ifdef CONFIG_RADIUS_TEST
os_free(data->dump_msk_file); os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
os_free(data->subscr_remediation_url);
os_free(data); os_free(data);
} }
@ -1682,9 +1721,13 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
{ {
struct radius_session *sess = ctx; struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server; struct radius_server_data *data = sess->server;
int ret;
return data->get_eap_user(data->conf_ctx, identity, identity_len, ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
phase2, user); phase2, user);
if (ret == 0 && user)
sess->remediation = user->remediation;
return ret;
} }

View File

@ -209,6 +209,9 @@ struct radius_server_conf {
#ifdef CONFIG_RADIUS_TEST #ifdef CONFIG_RADIUS_TEST
const char *dump_msk_file; const char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
char *subscr_remediation_url;
u8 subscr_remediation_method;
}; };