]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
HS 2.0R2: RADIUS server support to request Subscr Remediation
authorJouni Malinen <jouni@qca.qualcomm.com>
Wed, 21 Nov 2012 15:04:21 +0000 (17:04 +0200)
committerJouni Malinen <j@w1.fi>
Tue, 25 Feb 2014 23:24:25 +0000 (01:24 +0200)
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>

13 files changed:
hostapd/config_file.c
hostapd/hostapd.eap_user_sqlite
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/authsrv.c
src/ap/eap_user_db.c
src/ap/ieee802_1x.c
src/eap_server/eap.h
src/eapol_auth/eapol_auth_sm.c
src/eapol_auth/eapol_auth_sm.h
src/eapol_auth/eapol_auth_sm_i.h
src/radius/radius_server.c
src/radius/radius_server.h

index ca95747a5897f3205f2cd6d48fb6d398fb037f61..fa7d14a8ac36fb2209eeb01ccff1eed7096d55b6 100644 (file)
@@ -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)                                   \
index f688327103c8c838cb2911131eb2072c2c088141..2c1f130f8bcde4ba5a1047447cb7985826a7b1c3 100644 (file)
@@ -2,6 +2,7 @@ CREATE TABLE users(
        identity TEXT PRIMARY KEY,
        methods TEXT,
        password TEXT,
+       remediation TEXT,
        phase2 INTEGER
 );
 
index 07fd8c34c5978139831132fca4aa3ca024d26411..b995892cdad813e9e97c1b5794f9bf9ce4190ca5 100644 (file)
@@ -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);
index 7a40b3e90fbdfe431531eb1ec079b8e379977cf1..e1e34e2d512b93fb8b230f181a6caeaedfc8d452 100644 (file)
@@ -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_*) */
index 7183abae4742486554d59d81fb67ef5cab2f0014..7691012fe41b00470b7192b1fc21355c24369758 100644 (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->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) {
index 79d50e51694e6af5c775668a4631049b6ead7bbd..371a73f2a52f3695d4b60f50d5ed1126f52bdb1b 100644 (file)
@@ -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) {
index 19719083315a351a5a8bf852670f7c14bbc97a43..b12c9d6c72df38ab715229dbeb8bf345d3937ce2 100644 (file)
@@ -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 "
index 36b230b48cc464a87741d244da421b0439250d24..197b232ff295ba2f86b51f59e2e0a2ad05a57d09 100644 (file)
@@ -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} */
 };
index a2577814ec29db5f7de797a25740bac7cfd40dbf..525bdeef8071b0ef29946f5f27d0321e845557aa 100644 (file)
@@ -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;
 }
 
 
index f0ff4644f55434cb95eb3f8aecb300ef8326ec1a..320a0adbf148c9a4eb42f5172fec337e245462e8 100644 (file)
@@ -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);
index d7f893a1d666e57679379d21a21afeac8d28d1c4..25baddbab43159e25ddc1e92ec8e65952b1e5dfb 100644 (file)
@@ -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 */
index 2904b2f7f99d71613c3c94210e30c9962f0686ad..5074b6029d356e78da0b674976615effa65cd287 100644 (file)
@@ -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;
 }
 
 
index 78f5fc23a72cdb8754a8e0eb759cf100561ce4ab..e85d00902fb624f9a9147755227bdcedd0e87e2b 100644 (file)
@@ -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;
 };