]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FT: Add FT AP support for drivers that manage MLME internally
authorShan Palanisamy <Shan.Palanisamy@Atheros.com>
Mon, 25 Oct 2010 11:30:04 +0000 (14:30 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 1 Aug 2012 10:11:53 +0000 (13:11 +0300)
Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

src/ap/drv_callbacks.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c

index cf06a4fb8f4eece5909a62cf376b157a21c865f4..eeda19e7ed06cacd83c66abf780fe12a7ea453b4 100644 (file)
@@ -37,7 +37,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
        struct ieee802_11_elems elems;
        const u8 *ie;
        size_t ielen;
+#ifdef CONFIG_IEEE80211R
+       u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+       u8 *p = buf;
+#endif /* CONFIG_IEEE80211R */
        u16 reason = WLAN_REASON_UNSPECIFIED;
+       u16 status = WLAN_STATUS_SUCCESS;
 
        if (addr == NULL) {
                /*
@@ -146,27 +151,52 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                        return -1;
                }
                res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
-                                         ie, ielen, NULL, 0);
+                                         ie, ielen,
+                                         elems.mdie, elems.mdie_len);
                if (res != WPA_IE_OK) {
                        wpa_printf(MSG_DEBUG, "WPA/RSN information element "
                                   "rejected? (res %u)", res);
                        wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
-                       if (res == WPA_INVALID_GROUP)
+                       if (res == WPA_INVALID_GROUP) {
                                reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
-                       else if (res == WPA_INVALID_PAIRWISE)
+                               status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+                       } else if (res == WPA_INVALID_PAIRWISE) {
                                reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
-                       else if (res == WPA_INVALID_AKMP)
+                               status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+                       } else if (res == WPA_INVALID_AKMP) {
                                reason = WLAN_REASON_AKMP_NOT_VALID;
+                               status = WLAN_STATUS_AKMP_NOT_VALID;
+                       }
 #ifdef CONFIG_IEEE80211W
-                       else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+                       else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
                                reason = WLAN_REASON_INVALID_IE;
-                       else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+                               status = WLAN_STATUS_INVALID_IE;
+                       } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
                                reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+                               status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+                       }
 #endif /* CONFIG_IEEE80211W */
-                       else
+                       else {
                                reason = WLAN_REASON_INVALID_IE;
+                               status = WLAN_STATUS_INVALID_IE;
+                       }
                        goto fail;
                }
+#ifdef CONFIG_IEEE80211R
+               if (sta->auth_alg == WLAN_AUTH_FT) {
+                       status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+                                                        req_ies_len);
+                       if (status != WLAN_STATUS_SUCCESS) {
+                               if (status == WLAN_STATUS_INVALID_PMKID)
+                                       reason = WLAN_REASON_INVALID_IE;
+                               if (status == WLAN_STATUS_INVALID_MDIE)
+                                       reason = WLAN_REASON_INVALID_IE;
+                               if (status == WLAN_STATUS_INVALID_FTIE)
+                                       reason = WLAN_REASON_INVALID_IE;
+                               goto fail;
+                       }
+               }
+#endif /* CONFIG_IEEE80211R */
        } else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
                struct wpabuf *wps;
@@ -178,6 +208,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 #ifdef CONFIG_WPS_STRICT
                if (wps && wps_validate_assoc_req(wps) < 0) {
                        reason = WLAN_REASON_INVALID_IE;
+                       status = WLAN_STATUS_INVALID_IE;
                        wpabuf_free(wps);
                        goto fail;
                }
@@ -198,9 +229,24 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 skip_wpa_check:
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_IEEE80211R
+       p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+                                       sta->auth_alg, req_ies, req_ies_len);
+
+       hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#else /* CONFIG_IEEE80211R */
+       /* Keep compiler silent about unused variables */
+       if (status) {
+       }
+#endif /* CONFIG_IEEE80211R */
+
        new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
        sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
-       wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+       if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+               wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+       else
+               wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 
        hostapd_new_assoc_sta(hapd, sta, !new_assoc);
 
@@ -216,6 +262,9 @@ skip_wpa_check:
        return 0;
 
 fail:
+#ifdef CONFIG_IEEE80211R
+       hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R */
        hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
        ap_free_sta(hapd, sta);
        return -1;
@@ -324,8 +373,93 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+                                         const u8 *bssid,
+                                         u16 auth_transaction, u16 status,
+                                         const u8 *ies, size_t ies_len)
+{
+       struct hostapd_data *hapd = ctx;
+       struct sta_info *sta;
+
+       sta = ap_get_sta(hapd, dst);
+       if (sta == NULL)
+               return;
+
+       hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+       sta->flags |= WLAN_STA_AUTH;
+
+       hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 #ifdef HOSTAPD
 
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+                              struct auth_info *rx_auth)
+{
+       struct sta_info *sta;
+       u16 status = WLAN_STATUS_SUCCESS;
+       u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+       size_t resp_ies_len = 0;
+
+       sta = ap_get_sta(hapd, rx_auth->peer);
+       if (!sta) {
+               sta = ap_sta_add(hapd, rx_auth->peer);
+               if (sta == NULL) {
+                       status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+       sta->flags &= ~WLAN_STA_PREAUTH;
+       ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R
+       if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+               sta->auth_alg = WLAN_AUTH_FT;
+               if (sta->wpa_sm == NULL)
+                       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+                                                       sta->addr);
+               if (sta->wpa_sm == NULL) {
+                       wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+                                  "state machine");
+                       status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+                                   rx_auth->auth_transaction, rx_auth->ies,
+                                   rx_auth->ies_len,
+                                   hostapd_notify_auth_ft_finish, hapd);
+               return;
+       }
+#endif /* CONFIG_IEEE80211R */
+fail:
+       hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+                        status, resp_ies, resp_ies_len);
+}
+
+
+static void hostapd_action_rx(struct hostapd_data *hapd,
+                             struct rx_action *action)
+{
+       struct sta_info *sta;
+
+       sta = ap_get_sta(hapd, action->sa);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+               return;
+       }
+#ifdef CONFIG_IEEE80211R
+       if (action->category == WLAN_ACTION_FT) {
+               wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
+                          __func__, (int) action->len);
+               wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+       }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
 #ifdef NEED_AP_MLME
 
 #define HAPD_BROADCAST ((struct hostapd_data *) -1)
@@ -610,14 +744,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
                break;
-#ifdef NEED_AP_MLME
        case EVENT_RX_ACTION:
                if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
                    data->rx_action.bssid == NULL)
                        break;
+#ifdef NEED_AP_MLME
                hostapd_rx_action(hapd, &data->rx_action);
-               break;
 #endif /* NEED_AP_MLME */
+               hostapd_action_rx(hapd, &data->rx_action);
+               break;
+       case EVENT_AUTH:
+               hostapd_notif_auth(hapd, &data->auth);
+               break;
        case EVENT_CH_SWITCH:
                if (!data)
                        break;
index 1464ca90d74a8668263f786703ce663792c2bac8..6cfa00b7abae2bc4d35a836e1dc3e2682ab925d4 100644 (file)
@@ -159,6 +159,7 @@ struct wpa_auth_config {
        int ft_over_ds;
 #endif /* CONFIG_IEEE80211R */
        int disable_gtk;
+       int ap_mlme;
 };
 
 typedef enum {
@@ -197,6 +198,8 @@ struct wpa_auth_callbacks {
        struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
        int (*send_ft_action)(void *ctx, const u8 *dst,
                              const u8 *data, size_t data_len);
+       int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+                         size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
 };
 
index d2ec08868cf4270d22f292d19186a3e9ff0c27a9..dedc5fd2552aaa42aa7ab7db2165aa09a29c040b 100644 (file)
@@ -52,6 +52,19 @@ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
 }
 
 
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+                           const u8 *sta_addr,
+                           u8 *tspec_ie, size_t tspec_ielen)
+{
+       if (wpa_auth->cb.add_tspec == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+               return -1;
+       }
+       return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
+                                     tspec_ielen);
+}
+
+
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
 {
        u8 *pos = buf;
@@ -471,7 +484,8 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
 #endif /* CONFIG_IEEE80211W */
 
 
-static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+                               u8 *pos, u8 *end, u8 id, u8 descr_count,
                                const u8 *ies, size_t ies_len)
 {
        struct ieee802_11_elems parse;
@@ -504,7 +518,7 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
        }
 
 #ifdef NEED_AP_MLME
-       if (parse.wmm_tspec) {
+       if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
                struct wmm_tspec_element *tspec;
                int res;
 
@@ -541,13 +555,35 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
        }
 #endif /* NEED_AP_MLME */
 
+       if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+               struct wmm_tspec_element *tspec;
+               int res;
+
+               tspec = (struct wmm_tspec_element *) pos;
+               os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+               res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+                                      sizeof(*tspec));
+               if (res >= 0) {
+                       if (res)
+                               rdie->status_code = host_to_le16(res);
+                       else {
+                               /* TSPEC accepted; include updated TSPEC in
+                                * response */
+                               rdie->descr_count = 1;
+                               pos += sizeof(*tspec);
+                       }
+                       return pos;
+               }
+       }
+
        wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
        rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
        return pos;
 }
 
 
-static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+                              const u8 *ric, size_t ric_len)
 {
        const u8 *rpos, *start;
        const struct rsn_rdie *rdie;
@@ -569,7 +605,7 @@ static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
                                break;
                        rpos += 2 + rpos[1];
                }
-               pos = wpa_ft_process_rdie(pos, end, rdie->id,
+               pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
                                          rdie->descr_count,
                                          start, rpos - start);
        }
@@ -678,7 +714,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 
        ric_start = pos;
        if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
-               pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+               pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+                                        parse.ric_len);
                if (auth_alg == WLAN_AUTH_FT)
                        _ftie->mic_control[1] +=
                                ieee802_11_ie_count(ric_start,
@@ -1061,8 +1098,16 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
 
        if (os_memcmp(mic, ftie->mic, 16) != 0) {
                wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+               wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+                          MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
                wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
                wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+               wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+                           parse.mdie - 2, parse.mdie_len + 2);
+               wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+                           parse.ftie - 2, parse.ftie_len + 2);
+               wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+                           parse.rsn - 2, parse.rsn_len + 2);
                return WLAN_STATUS_INVALID_FTIE;
        }
 
index d6b1ca36d33e33234c807b68bdd6584edb4fc489..fa4182c41fb6c058f427072c44062272d351e51f 100644 (file)
@@ -428,6 +428,9 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
        struct hostapd_data *hapd = ctx;
        struct sta_info *sta;
 
+       if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+               return NULL;
+
        sta = ap_sta_add(hapd, sta_addr);
        if (sta == NULL)
                return NULL;
@@ -461,6 +464,14 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
                      len - sizeof(*ethhdr));
 }
 
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+                                     u8 *tspec_ie, size_t tspec_ielen)
+{
+       struct hostapd_data *hapd = ctx;
+       return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+}
+
 #endif /* CONFIG_IEEE80211R */
 
 
@@ -474,6 +485,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
        hostapd_wpa_auth_conf(hapd->conf, &_conf);
        if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
                _conf.tx_status = 1;
+       if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+               _conf.ap_mlme = 1;
        os_memset(&cb, 0, sizeof(cb));
        cb.ctx = hapd;
        cb.logger = hostapd_wpa_auth_logger;
@@ -492,6 +505,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
 #ifdef CONFIG_IEEE80211R
        cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
        cb.add_sta = hostapd_wpa_auth_add_sta;
+       cb.add_tspec = hostapd_wpa_auth_add_tspec;
 #endif /* CONFIG_IEEE80211R */
        hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
        if (hapd->wpa_auth == NULL) {