From 88b32a99d30894b2d6bb391371c442fc117edbab Mon Sep 17 00:00:00 2001 From: Shan Palanisamy Date: Mon, 25 Oct 2010 14:30:04 +0300 Subject: [PATCH] FT: Add FT AP support for drivers that manage MLME internally Signed-hostap: Jouni Malinen --- src/ap/drv_callbacks.c | 158 ++++++++++++++++++++++++++++++++++++++--- src/ap/wpa_auth.h | 3 + src/ap/wpa_auth_ft.c | 55 ++++++++++++-- src/ap/wpa_auth_glue.c | 14 ++++ 4 files changed, 215 insertions(+), 15 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index cf06a4fb8..eeda19e7e 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -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; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 1464ca90d..6cfa00b7a 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -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 */ }; diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index d2ec08868..dedc5fd25 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -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; } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index d6b1ca36d..fa4182c41 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -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) {