HS 2.0R2 AP: Add support for deauthentication request
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 26 Jul 2013 19:13:58 +0000 (22:13 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 25 Feb 2014 23:24:24 +0000 (01:24 +0200)
If the RADIUS server includes deauthentication request in Access-Accept,
send a WNM-Notification frame to the station after 4-way handshake and
disconnect the station after configurable timeout.

A new control interface command, WNM_DEAUTH_REQ, is added for testing
purposes to allow the notification frame to sent based on local request.
This case does not disconnect the station automatically, i.e., a
separate control interface command would be needed for that.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

hostapd/config_file.c
hostapd/ctrl_iface.c
hostapd/hostapd.conf
hostapd/hostapd_cli.c
src/ap/ap_config.h
src/ap/hs20.c
src/ap/hs20.h
src/ap/ieee802_1x.c
src/ap/sta_info.c
src/ap/sta_info.h
src/radius/radius.h

index d38c3f8..be05840 100644 (file)
@@ -2814,6 +2814,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        bss->osen = atoi(pos);
                } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
                        bss->anqp_domain_id = atoi(pos);
+               } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+                       bss->hs20_deauth_req_timeout = atoi(pos);
                } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
                        if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
                                errors++;
index 53d8d3d..dbdc8c6 100644 (file)
@@ -642,6 +642,56 @@ static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
        return hs20_send_wnm_notification(hapd, addr, 1, url);
 }
 
+
+static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
+                                             const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       int code, reauth_delay, ret;
+       const char *pos;
+       size_t url_len;
+       struct wpabuf *req;
+
+       /* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       code = atoi(pos);
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       reauth_delay = atoi(pos);
+
+       url_len = 0;
+       pos = os_strchr(pos, ' ');
+       if (pos) {
+               pos++;
+               url_len = os_strlen(pos);
+       }
+
+       req = wpabuf_alloc(4 + url_len);
+       if (req == NULL)
+               return -1;
+       wpabuf_put_u8(req, code);
+       wpabuf_put_le16(req, reauth_delay);
+       wpabuf_put_u8(req, url_len);
+       if (pos)
+               wpabuf_put_data(req, pos, url_len);
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
+                  " to indicate imminent deauthentication (code=%d "
+                  "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
+       ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
+       wpabuf_free(req);
+       return ret;
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -1379,6 +1429,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
                if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
+               if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
+                       reply_len = -1;
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_WNM
        } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
index 332cee0..2903df8 100644 (file)
@@ -1590,6 +1590,13 @@ own_ip_addr=127.0.0.1
 # information. 0 = Some of the ANQP information is unique to this AP (default).
 #anqp_domain_id=1234
 
+# Deauthentication request timeout
+# If the RADIUS server indicates that the station is not allowed to connect to
+# the BSS/ESS, the AP can allow the station some time to download a
+# notification page (URL included in the message). This parameter sets that
+# timeout in seconds.
+#hs20_deauth_req_timeout=60
+
 # Operator Friendly Name
 # This parameter can be used to configure one or more Operator Friendly Name
 # Duples. Each entry has a two or three character language code (ISO-639)
index 4d5f269..8caca4f 100644 (file)
@@ -763,6 +763,31 @@ static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
+                                          char *argv[])
+{
+       char buf[300];
+       int res;
+
+       if (argc < 3) {
+               printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
+               return -1;
+       }
+
+       if (argc > 3)
+               res = os_snprintf(buf, sizeof(buf),
+                                 "HS20_DEAUTH_REQ %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else
+               res = os_snprintf(buf, sizeof(buf),
+                                 "HS20_DEAUTH_REQ %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       if (res < 0 || res >= (int) sizeof(buf))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        hostapd_cli_quit = 1;
@@ -962,6 +987,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
        { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
        { "chan_switch", hostapd_cli_cmd_chan_switch },
        { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+       { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
        { NULL, NULL }
 };
 
index 73645cd..2eea266 100644 (file)
@@ -465,6 +465,7 @@ struct hostapd_bss_config {
        size_t hs20_connection_capability_len;
        u8 *hs20_operating_class;
        u8 hs20_operating_class_len;
+       unsigned int hs20_deauth_req_timeout;
 #endif /* CONFIG_HS20 */
 
        u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
index d521d2c..d7909fa 100644 (file)
@@ -140,3 +140,38 @@ int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
 
        return ret;
 }
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+                                         const u8 *addr,
+                                         const struct wpabuf *payload)
+{
+       struct wpabuf *buf;
+       int ret;
+
+       /* TODO: should refuse to send notification if the STA is not associated
+        * or if the STA did not indicate support for WNM-Notification */
+
+       buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+       if (buf == NULL)
+               return -1;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+       wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+       wpabuf_put_u8(buf, 1); /* Dialog token */
+       wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+       /* Deauthentication Imminent Notice subelement */
+       wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+       wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+       wpabuf_put_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+       wpabuf_put_buf(buf, payload);
+
+       ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+                                     wpabuf_head(buf), wpabuf_len(buf));
+
+       wpabuf_free(buf);
+
+       return ret;
+}
index c7ffa36..152439f 100644 (file)
@@ -15,5 +15,8 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
 int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
                               u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+                                         const u8 *addr,
+                                         const struct wpabuf *payload);
 
 #endif /* HS20_H */
index f07b6bf..3c7b1ed 100644 (file)
@@ -1265,6 +1265,27 @@ static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
        /* TODO: assign the STA into remediation VLAN or add filtering */
 }
 
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+                                      struct sta_info *sta, u8 *pos,
+                                      size_t len)
+{
+       if (len < 3)
+               return; /* Malformed information */
+       sta->hs20_deauth_requested = 1;
+       wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u  "
+                  "Re-auth Delay %u",
+                  *pos, WPA_GET_LE16(pos + 1));
+       wpabuf_free(sta->hs20_deauth_req);
+       sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+       if (sta->hs20_deauth_req) {
+               wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+               wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
+               wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+       }
+       ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -1278,6 +1299,8 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
 
        buf = NULL;
        sta->remediation = 0;
+       sta->hs20_deauth_requested = 0;
+
        for (;;) {
                if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
                                            &buf, &len, buf) < 0)
@@ -1302,6 +1325,9 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
                case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
                        ieee802_1x_hs20_sub_rem(sta, pos, sublen);
                        break;
+               case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+                       ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+                       break;
                }
        }
 #endif /* CONFIG_HS20 */
@@ -1468,6 +1494,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
                ieee802_1x_update_sta_cui(hapd, sta, msg);
                ieee802_1x_check_hs20(hapd, sta, msg);
                if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+                   !sta->hs20_deauth_requested &&
                    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
                                       session_timeout_set ?
                                       (int) session_timeout : -1, sm) == 0) {
@@ -2234,11 +2261,20 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
                        os_free(sta->remediation_url);
                        sta->remediation_url = NULL;
                }
+
+               if (sta->hs20_deauth_req) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+                                  "to " MACSTR " to indicate imminent "
+                                  "deauthentication", MAC2STR(sta->addr));
+                       hs20_send_wnm_notification_deauth_req(
+                               hapd, sta->addr, sta->hs20_deauth_req);
+               }
        }
 #endif /* CONFIG_HS20 */
 
        key = ieee802_1x_get_key(sta->eapol_sm, &len);
        if (success && key && len >= PMK_LEN && !sta->remediation &&
+           !sta->hs20_deauth_requested &&
            wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
                               sta->eapol_sm) == 0) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
index 811a42a..ada1106 100644 (file)
@@ -266,6 +266,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        os_free(sta->identity);
        os_free(sta->radius_cui);
        os_free(sta->remediation_url);
+       wpabuf_free(sta->hs20_deauth_req);
 
 #ifdef CONFIG_SAE
        sae_clear_data(sta->sae);
index a784439..df86cc2 100644 (file)
@@ -58,6 +58,7 @@ struct sta_info {
        unsigned int no_p2p_set:1;
        unsigned int qos_map_enabled:1;
        unsigned int remediation:1;
+       unsigned int hs20_deauth_requested:1;
 
        u16 auth_alg;
        u8 previous_ap[6];
@@ -128,6 +129,7 @@ struct sta_info {
        struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
        u8 remediation_method;
        char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+       struct wpabuf *hs20_deauth_req;
 
        struct os_reltime connected_time;
 
index b39aa7b..8cb7a0f 100644 (file)
@@ -171,6 +171,7 @@ enum {
        RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
        RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
        RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
+       RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
 };
 
 #ifdef _MSC_VER