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:
parent
ae6d15c722
commit
8d2a9921af
13 changed files with 98 additions and 14 deletions
|
@ -3075,6 +3075,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
|||
} else if (os_strcmp(buf, "osu_service_desc") == 0) {
|
||||
if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
|
||||
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 */
|
||||
#ifdef CONFIG_TESTING_OPTIONS
|
||||
#define PARSE_TEST_PROBABILITY(_val) \
|
||||
|
|
|
@ -2,6 +2,7 @@ CREATE TABLE users(
|
|||
identity TEXT PRIMARY KEY,
|
||||
methods TEXT,
|
||||
password TEXT,
|
||||
remediation TEXT,
|
||||
phase2 INTEGER
|
||||
);
|
||||
|
||||
|
|
|
@ -546,6 +546,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
|
|||
}
|
||||
os_free(conf->hs20_osu_providers);
|
||||
}
|
||||
os_free(conf->subscr_remediation_url);
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
wpabuf_free(conf->vendor_elements);
|
||||
|
|
|
@ -126,6 +126,7 @@ struct hostapd_eap_user {
|
|||
unsigned int wildcard_prefix:1;
|
||||
unsigned int password_hash:1; /* whether password is hashed with
|
||||
* nt_password_hash() */
|
||||
unsigned int remediation:1;
|
||||
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
|
||||
};
|
||||
|
||||
|
@ -489,6 +490,8 @@ struct hostapd_bss_config {
|
|||
} *hs20_osu_providers, *last_osu;
|
||||
size_t hs20_osu_providers_count;
|
||||
unsigned int hs20_deauth_req_timeout;
|
||||
char *subscr_remediation_url;
|
||||
u8 subscr_remediation_method;
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
|
||||
|
|
|
@ -80,6 +80,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
|
|||
}
|
||||
user->force_version = eap_user->force_version;
|
||||
user->ttls_auth = eap_user->ttls_auth;
|
||||
user->remediation = eap_user->remediation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -116,6 +117,10 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
|
|||
#ifdef CONFIG_RADIUS_TEST
|
||||
srv.dump_msk_file = conf->dump_msk_file;
|
||||
#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);
|
||||
if (hapd->radius_srv == NULL) {
|
||||
|
|
|
@ -89,6 +89,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
|
|||
user->next = (void *) 1;
|
||||
} else if (os_strcmp(col[i], "methods") == 0 && 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),
|
||||
"SELECT password,methods FROM users WHERE "
|
||||
"identity='%s' AND phase2=%d;", id_str, phase2);
|
||||
"SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
|
||||
id_str, phase2);
|
||||
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
|
||||
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
|
||||
SQLITE_OK) {
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
|
||||
|
||||
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,
|
||||
|
@ -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,
|
||||
int preauth)
|
||||
int preauth, int remediation)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
struct sta_info *sta = sta_ctx;
|
||||
if (preauth)
|
||||
rsn_preauth_finished(hapd, sta, success);
|
||||
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->ttls_auth = eap_user->ttls_auth;
|
||||
user->remediation = eap_user->remediation;
|
||||
|
||||
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,
|
||||
struct sta_info *sta, int success)
|
||||
struct sta_info *sta, int success,
|
||||
int remediation)
|
||||
{
|
||||
const u8 *key;
|
||||
size_t len;
|
||||
|
@ -2298,6 +2301,14 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
|
|||
static const int dot11RSNAConfigPMKLifetime = 43200;
|
||||
|
||||
#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 (sta->remediation) {
|
||||
wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
|
||||
|
|
|
@ -32,6 +32,7 @@ struct eap_user {
|
|||
* nt_password_hash() */
|
||||
int phase2;
|
||||
int force_version;
|
||||
unsigned int remediation:1;
|
||||
int ttls_auth; /* bitfield of
|
||||
* EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
|
||||
};
|
||||
|
|
|
@ -219,7 +219,8 @@ SM_STATE(AUTH_PAE, DISCONNECTED)
|
|||
sm->eapolLogoff = FALSE;
|
||||
if (!from_initialize) {
|
||||
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));
|
||||
}
|
||||
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),
|
||||
extra);
|
||||
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 eapol_state_machine *sm = ctx;
|
||||
return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
|
||||
identity_len, phase2, user);
|
||||
int ret;
|
||||
|
||||
ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
|
||||
identity_len, phase2, user);
|
||||
if (user->remediation)
|
||||
sm->remediation = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -60,7 +60,8 @@ struct eapol_auth_cb {
|
|||
size_t datalen);
|
||||
void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
|
||||
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 phase2, struct eap_user *user);
|
||||
int (*sta_entry_alive)(void *ctx, const u8 *addr);
|
||||
|
|
|
@ -173,6 +173,8 @@ struct eapol_state_machine {
|
|||
struct eapol_authenticator *eapol;
|
||||
|
||||
void *sta; /* station context pointer to use in callbacks */
|
||||
|
||||
int remediation;
|
||||
};
|
||||
|
||||
#endif /* EAPOL_AUTH_SM_I_H */
|
||||
|
|
|
@ -77,6 +77,8 @@ struct radius_session {
|
|||
u8 last_identifier;
|
||||
struct radius_msg *last_reply;
|
||||
u8 last_authenticator[16];
|
||||
|
||||
unsigned int remediation:1;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -307,6 +309,9 @@ struct radius_server_data {
|
|||
#ifdef CONFIG_RADIUS_TEST
|
||||
char *dump_msk_file;
|
||||
#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) {
|
||||
RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
|
||||
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
|
||||
if (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
|
||||
os_free(data->dump_msk_file);
|
||||
#endif /* CONFIG_RADIUS_TEST */
|
||||
os_free(data->subscr_remediation_url);
|
||||
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_server_data *data = sess->server;
|
||||
int ret;
|
||||
|
||||
return data->get_eap_user(data->conf_ctx, identity, identity_len,
|
||||
phase2, user);
|
||||
ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
|
||||
phase2, user);
|
||||
if (ret == 0 && user)
|
||||
sess->remediation = user->remediation;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -209,6 +209,9 @@ struct radius_server_conf {
|
|||
#ifdef CONFIG_RADIUS_TEST
|
||||
const char *dump_msk_file;
|
||||
#endif /* CONFIG_RADIUS_TEST */
|
||||
|
||||
char *subscr_remediation_url;
|
||||
u8 subscr_remediation_method;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue