From 8d2a9921af3ebbeb25e175386f6b809ac56defd5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 21 Nov 2012 17:04:21 +0200 Subject: [PATCH] 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 --- hostapd/config_file.c | 5 ++++ hostapd/hostapd.eap_user_sqlite | 1 + src/ap/ap_config.c | 1 + src/ap/ap_config.h | 3 ++ src/ap/authsrv.c | 5 ++++ src/ap/eap_user_db.c | 6 ++-- src/ap/ieee802_1x.c | 19 ++++++++++--- src/eap_server/eap.h | 1 + src/eapol_auth/eapol_auth_sm.c | 16 +++++++---- src/eapol_auth/eapol_auth_sm.h | 3 +- src/eapol_auth/eapol_auth_sm_i.h | 2 ++ src/radius/radius_server.c | 47 ++++++++++++++++++++++++++++++-- src/radius/radius_server.h | 3 ++ 13 files changed, 98 insertions(+), 14 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index ca95747a5..fa7d14a8a 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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) \ diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite index f68832710..2c1f130f8 100644 --- a/hostapd/hostapd.eap_user_sqlite +++ b/hostapd/hostapd.eap_user_sqlite @@ -2,6 +2,7 @@ CREATE TABLE users( identity TEXT PRIMARY KEY, methods TEXT, password TEXT, + remediation TEXT, phase2 INTEGER ); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 07fd8c34c..b995892cd 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 7a40b3e90..e1e34e2d5 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -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_*) */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 7183abae4..7691012fe 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -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) { diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 79d50e516..371a73f2a 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -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) { diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 197190833..b12c9d6c7 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -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 " diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 36b230b48..197b232ff 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -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} */ }; diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index a2577814e..525bdeef8 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -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; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index f0ff4644f..320a0adbf 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -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); diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index d7f893a1d..25baddbab 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -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 */ diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 2904b2f7f..5074b6029 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -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; } diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 78f5fc23a..e85d00902 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -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; };