Browse Source

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>
Jouni Malinen 12 years ago
parent
commit
8d2a9921af

+ 5 - 0
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)					\

+ 1 - 0
hostapd/hostapd.eap_user_sqlite

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

+ 1 - 0
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);

+ 3 - 0
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_*) */

+ 5 - 0
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) {

+ 4 - 2
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) {

+ 15 - 4
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 "

+ 1 - 0
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} */
 };

+ 11 - 5
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;
 }
 
 

+ 2 - 1
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);

+ 2 - 0
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 */

+ 45 - 2
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;
 }
 
 

+ 3 - 0
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;
 };