diff --git a/hostapd/wpa_ft.c b/hostapd/wpa_ft.c index 9cf6713ff..337c7ebee 100644 --- a/hostapd/wpa_ft.c +++ b/hostapd/wpa_ft.c @@ -571,6 +571,8 @@ struct wpa_ft_ies { const u8 *rsn; size_t rsn_len; const u8 *rsn_pmkid; + const u8 *ric; + size_t ric_len; }; @@ -623,6 +625,8 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, const u8 *end, *pos; struct wpa_ie_data data; int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) @@ -651,14 +655,60 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->mdie_len = pos[1]; break; case WLAN_EID_FAST_BSS_TRANSITION: + if (pos[1] < sizeof(*ftie)) + return -1; + ftie = (const struct rsn_ftie *) (pos + 2); + prot_ie_count = ftie->mic_control[1]; if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) return -1; break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; } pos += 2 + pos[1]; } + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + return 0; } @@ -937,20 +987,11 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_FTIE; } - /* - * Assume that MDIE, FTIE, and RSN IE are protected and that there is - * no RIC, so total of 3 protected IEs. - */ - if (ftie->mic_control[1] != 3) { - wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", - ftie->mic_control[1]); - return WLAN_STATUS_INVALID_FTIE; - } - if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, - parse.rsn - 2, parse.rsn_len + 2, NULL, 0, + parse.rsn - 2, parse.rsn_len + 2, + parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return WLAN_STATUS_UNSPECIFIED_FAILURE; diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 242f933b0..6910b4e63 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -257,3 +257,25 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, return unknown ? ParseUnknown : ParseOK; } + + +int ieee802_11_ie_count(const u8 *ies, size_t ies_len) +{ + int count = 0; + const u8 *pos, *end; + + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + + while (pos + 2 <= end) { + if (pos + 2 + pos[1] > end) + break; + count++; + pos += 2 + pos[1]; + } + + return count; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index b7e497b6c..14599cb4f 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -70,5 +70,6 @@ typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; ParseRes ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors); +int ieee802_11_ie_count(const u8 *ies, size_t ies_len); #endif /* IEEE802_11_COMMON_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 342d03221..39f100018 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1342,6 +1342,10 @@ union wpa_event_data { size_t ies_len; int ft_action; u8 target_ap[ETH_ALEN]; + /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ + const u8 *ric_ies; + /** Length of ric_ies buffer in octets */ + size_t ric_ies_len; } ft_ies; /** diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index f97d9c5f5..29a80afeb 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -284,7 +284,8 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, const u8 *r1kh_id); int wpa_ft_prepare_auth_request(struct wpa_sm *sm); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, - int ft_action, const u8 *target_ap); + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index c89b89a77..014261c78 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -20,6 +20,7 @@ #include "wpa_ie.h" #include "aes_wrap.h" #include "ieee802_11_defs.h" +#include "ieee802_11_common.h" #ifdef CONFIG_IEEE80211R @@ -105,20 +106,23 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, /** - * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth Request + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @len: Buffer for returning the length of the IEs * @anonce: ANonce or %NULL if not yet available * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List * @kck: 128-bit KCK for MIC or %NULL if no MIC is used * @target_ap: Target AP address + * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL + * @ric_ies_len: Length of ric_ies buffer in octets * Returns: Pointer to buffer with IEs or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(); */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, - const u8 *kck, const u8 *target_ap) + const u8 *kck, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len) { size_t buf_len; u8 *buf, *pos, *ftie_len, *ftie_pos; @@ -130,13 +134,13 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, sm->ft_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + - 2 + sm->r0kh_id_len + 100; + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) return NULL; pos = buf; - /* RSNIE[PMKR0Name] */ + /* RSNIE[PMKR0Name/PMKR1Name] */ rsnie = (struct rsn_ie_hdr *) pos; rsnie->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(rsnie->version, RSN_VERSION); @@ -241,6 +245,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += sm->r0kh_id_len; *ftie_len = pos - ftie_len - 1; + if (ric_ies) { + /* RIC Request */ + os_memcpy(pos, ric_ies, ric_ies_len); + pos += ric_ies_len; + } + if (kck) { /* * IEEE Std 802.11r-2008, 11A.8.4 @@ -253,12 +263,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, * FTIE (with MIC field set to 0) * RIC-Request (if present) */ - ftie->mic_control[1] = 3; /* Information element count */ + /* Information element count */ + ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, + ric_ies_len); if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, - (u8 *) rsnie, 2 + rsnie->len, NULL, 0, - ftie->mic) < 0) { + (u8 *) rsnie, 2 + rsnie->len, ric_ies, + ric_ies_len, ftie->mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; @@ -440,7 +452,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, sm->bssid); + NULL, sm->bssid, NULL, 0); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -452,7 +464,8 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm) int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, - int ft_action, const u8 *target_ap) + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; size_t ft_ies_len; @@ -464,6 +477,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { @@ -553,7 +567,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, - sm->pmk_r1_name, sm->ptk.kck, bssid); + sm->pmk_r1_name, sm->ptk.kck, bssid, + ric_ies, ric_ies_len); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -857,7 +872,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, target_ap); + NULL, target_ap, NULL, 0); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index bd8f09e94..a150455a5 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -111,6 +111,12 @@ static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) *pos = data; } +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index e6f3a4aed..b47ea9d1d 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1087,7 +1087,9 @@ wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies, data->ft_ies.ies_len, data->ft_ies.ft_action, - data->ft_ies.target_ap) < 0) { + data->ft_ies.target_ap, + data->ft_ies.ric_ies, + data->ft_ies.ric_ies_len) < 0) { /* TODO: prevent MLME/driver from trying to associate? */ } } diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c index fd1fa5581..3576365d6 100644 --- a/wpa_supplicant/mlme.c +++ b/wpa_supplicant/mlme.c @@ -94,6 +94,7 @@ static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s); static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s); static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx); static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx); +static void ieee80211_build_tspec(struct wpabuf *buf); static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s, @@ -876,12 +877,36 @@ static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s, case WLAN_AUTH_FT: { union wpa_event_data data; + struct wpabuf *ric = NULL; os_memset(&data, 0, sizeof(data)); data.ft_ies.ies = mgmt->u.auth.variable; data.ft_ies.ies_len = len - (mgmt->u.auth.variable - (u8 *) mgmt); os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN); + if (os_strcmp(wpa_s->driver->name, "test") == 0 && + wpa_s->mlme.wmm_enabled) { + ric = wpabuf_alloc(200); + if (ric) { + /* Build simple RIC-Request: RDIE | TSPEC */ + + /* RIC Data (RDIE) */ + wpabuf_put_u8(ric, WLAN_EID_RIC_DATA); + wpabuf_put_u8(ric, 4); + wpabuf_put_u8(ric, 0); /* RDIE Identifier */ + wpabuf_put_u8(ric, 1); /* Resource Descriptor + * Count */ + wpabuf_put_le16(ric, 0); /* Status Code */ + + /* WMM TSPEC */ + ieee80211_build_tspec(ric); + + data.ft_ies.ric_ies = wpabuf_head(ric); + data.ft_ies.ric_ies_len = wpabuf_len(ric); + } + } + wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data); + wpabuf_free(ric); ieee80211_auth_completed(wpa_s); break; } @@ -1012,33 +1037,11 @@ static int ieee80211_ft_assoc_resp(struct wpa_supplicant *wpa_s, } -static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) +static void ieee80211_build_tspec(struct wpabuf *buf) { - struct wpabuf *buf; - struct ieee80211_mgmt *mgmt; struct wmm_tspec_element *tspec; - size_t alen; int tid, up; - wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC"); - mgmt = NULL; - alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; - - buf = wpabuf_alloc(alen + sizeof(*tspec)); - if (buf == NULL) - return; - - mgmt = wpabuf_put(buf, alen); - os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); - os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - mgmt->u.action.category = WLAN_ACTION_WMM; - mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ; - mgmt->u.action.u.wmm_action.dialog_token = 1; - mgmt->u.action.u.wmm_action.status_code = 0; - tspec = wpabuf_put(buf, sizeof(*tspec)); tspec->eid = WLAN_EID_VENDOR_SPECIFIC; tspec->length = sizeof(*tspec) - 2; @@ -1059,6 +1062,35 @@ static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) tspec->mean_data_rate = host_to_le32(128000); /* bits per second */ tspec->minimum_phy_rate = host_to_le32(6000000); tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */ +} + + +static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + struct ieee80211_mgmt *mgmt; + size_t alen; + + wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC"); + mgmt = NULL; + alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt; + + buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element)); + if (buf == NULL) + return; + + mgmt = wpabuf_put(buf, alen); + os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); + os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WMM; + mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ; + mgmt->u.action.u.wmm_action.dialog_token = 1; + mgmt->u.action.u.wmm_action.status_code = 0; + + ieee80211_build_tspec(buf); ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); @@ -1216,7 +1248,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, ieee80211_associated(wpa_s); - if (os_strcmp(wpa_s->driver->name, "test") == 0 && + if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT && + os_strcmp(wpa_s->driver->name, "test") == 0 && elems.wmm && wpa_s->mlme.wmm_enabled) { /* Test WMM-AC - send ADDTS for WMM TSPEC */ ieee80211_tx_addts(wpa_s);