From babfbf15cc3cd500fd032b97935ca43f6931e682 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 9 Mar 2009 20:45:17 +0200 Subject: [PATCH] FT: Add RIC Request generation and validation (but not processing) This adds first part of FT resource request as part of Reassocition Request frame (i.e., FT Protocol, not FT Resource Request Protocol). wpa_supplicant can generate a test resource request when driver_test.c is used with internal MLME code and hostapd can verify the FTIE MIC properly with the included RIC Request. The actual RIC Request IEs are not processed yet and hostapd does not yet reply with RIC Response (nor would wpa_supplicant be able to validate the FTIE MIC for a frame with RIC Response). --- hostapd/wpa_ft.c | 63 +++++++++++++++++++++----- src/common/ieee802_11_common.c | 22 +++++++++ src/common/ieee802_11_common.h | 1 + src/drivers/driver.h | 4 ++ src/rsn_supp/wpa.h | 3 +- src/rsn_supp/wpa_ft.c | 37 +++++++++++----- src/utils/wpabuf.h | 6 +++ wpa_supplicant/events.c | 4 +- wpa_supplicant/mlme.c | 81 ++++++++++++++++++++++++---------- 9 files changed, 173 insertions(+), 48 deletions(-) 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);