From f238cf9f421bd151067d327c1593377b954282ee Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 9 Mar 2009 22:25:58 +0200 Subject: [PATCH] FT: Add RIC Request processing and RIC Response generation hostapd will now go through the RIC Request and process each RDIE. Only WMM TSPEC requests are currently supported; all other request descriptors will be declined. RIC Response is written by hostapd and verified by wpa_supplicant (MIC validation). wpa_supplicant does not yet have code to notify the driver about the resource request results. --- hostapd/ieee802_11.c | 7 +- hostapd/wme.c | 61 +++++++------ hostapd/wme.h | 1 + hostapd/wpa.h | 3 +- hostapd/wpa_ft.c | 165 ++++++++++++++++++++++++++++++----- src/common/ieee802_11_defs.h | 4 + src/common/wpa_common.h | 6 ++ src/rsn_supp/wpa_ft.c | 67 ++++++++++++-- 8 files changed, 252 insertions(+), 62 deletions(-) diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c index 83e4388a8..72d9f61fa 100644 --- a/hostapd/ieee802_11.c +++ b/hostapd/ieee802_11.c @@ -675,7 +675,7 @@ static void handle_assoc(struct hostapd_data *hapd, int send_deauth = 0, send_len, left, i; struct sta_info *sta; struct ieee802_11_elems elems; - u8 buf[sizeof(struct ieee80211_mgmt) + 512]; + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; struct ieee80211_mgmt *reply; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : @@ -1133,10 +1133,11 @@ static void handle_assoc(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211R if (resp == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS - * Transition Information, RSN */ + * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, buf + sizeof(buf) - p, - sta->auth_alg); + sta->auth_alg, + pos, left); } #endif /* CONFIG_IEEE80211R */ diff --git a/hostapd/wme.c b/hostapd/wme.c index 700410cc8..7f92a0a49 100644 --- a/hostapd/wme.c +++ b/hostapd/wme.c @@ -165,25 +165,12 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, } -static void wmm_addts_req(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - struct wmm_tspec_element *tspec, size_t len) +int wmm_process_tspec(struct wmm_tspec_element *tspec) { - u8 *end = ((u8 *) mgmt) + len; int medium_time, pps, duration; int up, psb, dir, tid; u16 val, surplus; - if ((u8 *) (tspec + 1) > end) { - wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); - return; - } - - wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " - "from " MACSTR, - mgmt->u.action.u.wmm_action.dialog_token, - MAC2STR(mgmt->sa)); - up = (tspec->ts_info[1] >> 3) & 0x07; psb = (tspec->ts_info[1] >> 2) & 0x01; dir = (tspec->ts_info[0] >> 5) & 0x03; @@ -204,7 +191,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, val = le_to_host16(tspec->nominal_msdu_size); if (val == 0) { wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); - goto invalid; + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; @@ -213,7 +200,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); - goto invalid; + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / @@ -226,7 +213,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (surplus <= 0x2000) { wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " "greater than unity"); - goto invalid; + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } medium_time = surplus * pps * duration / 0x2000; @@ -241,26 +228,38 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (medium_time > 750000) { wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " "75%% of available bandwidth"); - wmm_send_action(hapd, mgmt->sa, tspec, - WMM_ACTION_CODE_ADDTS_RESP, - mgmt->u.action.u.wmm_action.dialog_token, - WMM_ADDTS_STATUS_REFUSED); - return; + return WMM_ADDTS_STATUS_REFUSED; } /* Convert to 32 microseconds per second unit */ tspec->medium_time = host_to_le16(medium_time / 32); - wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, - mgmt->u.action.u.wmm_action.dialog_token, - WMM_ADDTS_STATUS_ADMISSION_ACCEPTED); - return; + return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED; +} -invalid: - wmm_send_action(hapd, mgmt->sa, tspec, - WMM_ACTION_CODE_ADDTS_RESP, - mgmt->u.action.u.wmm_action.dialog_token, - WMM_ADDTS_STATUS_INVALID_PARAMETERS); + +static void wmm_addts_req(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + struct wmm_tspec_element *tspec, size_t len) +{ + u8 *end = ((u8 *) mgmt) + len; + int res; + + if ((u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " + "from " MACSTR, + mgmt->u.action.u.wmm_action.dialog_token, + MAC2STR(mgmt->sa)); + + res = wmm_process_tspec(tspec); + wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res); + + wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, res); } diff --git a/hostapd/wme.h b/hostapd/wme.h index 2f851aedc..1ddb647d8 100644 --- a/hostapd/wme.h +++ b/hostapd/wme.h @@ -31,5 +31,6 @@ static inline int hostapd_wmm_sta_config(struct hostapd_data *hapd, #endif /* NEED_MLME */ void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len); +int wmm_process_tspec(struct wmm_tspec_element *tspec); #endif /* WME_H */ diff --git a/hostapd/wpa.h b/hostapd/wpa.h index 7d9b3d310..873bc4229 100644 --- a/hostapd/wpa.h +++ b/hostapd/wpa.h @@ -266,7 +266,8 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, - size_t max_len, int auth_alg); + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len); void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 auth_transaction, const u8 *ies, size_t ies_len, void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, diff --git a/hostapd/wpa_ft.c b/hostapd/wpa_ft.c index 337c7ebee..5a5a44314 100644 --- a/hostapd/wpa_ft.c +++ b/hostapd/wpa_ft.c @@ -19,6 +19,7 @@ #include "wpa.h" #include "aes_wrap.h" #include "ieee802_11.h" +#include "wme.h" #include "defs.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" @@ -26,6 +27,28 @@ #ifdef CONFIG_IEEE80211R +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *ric; + size_t ric_len; +}; + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse); + + static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { @@ -471,14 +494,122 @@ 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, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems parse; + struct rsn_rdie *rdie; + + wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d", + id, descr_count); + wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)", + ies, ies_len); + + if (end - pos < (int) sizeof(*rdie)) { + wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE"); + return pos; + } + + *pos++ = WLAN_EID_RIC_DATA; + *pos++ = sizeof(*rdie); + rdie = (struct rsn_rdie *) pos; + rdie->id = id; + rdie->descr_count = 0; + rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS); + pos += sizeof(*rdie); + + if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs"); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + + if (parse.wmm_tspec) { + struct wmm_tspec_element *tspec; + int res; + + if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { + wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " + "(%d)", (int) parse.wmm_tspec_len); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + if (end - pos < (int) sizeof(*tspec)) { + wpa_printf(MSG_ERROR, "FT: Not enough room for " + "response TSPEC"); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + tspec = (struct wmm_tspec_element *) pos; + os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); + res = wmm_process_tspec(tspec); + wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); + if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) + rdie->status_code = + host_to_le16(WLAN_STATUS_INVALID_PARAMETERS); + else if (res == WMM_ADDTS_STATUS_REFUSED) + rdie->status_code = + host_to_le16(WLAN_STATUS_REQUEST_DECLINED); + 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) +{ + const u8 *rpos, *start; + const struct rsn_rdie *rdie; + + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len); + + rpos = ric; + while (rpos + sizeof(*rdie) < ric + ric_len) { + if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) || + rpos + 2 + rpos[1] > ric + ric_len) + break; + rdie = (const struct rsn_rdie *) (rpos + 2); + rpos += 2 + rpos[1]; + start = rpos; + + while (rpos + 2 <= ric + ric_len && + rpos + 2 + rpos[1] <= ric + ric_len) { + if (rpos[0] == WLAN_EID_RIC_DATA) + break; + rpos += 2 + rpos[1]; + } + pos = wpa_ft_process_rdie(pos, end, rdie->id, + rdie->descr_count, + start, rpos - start); + } + + return pos; +} + + u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, - size_t max_len, int auth_alg) + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len) { u8 *end, *mdie, *ftie, *rsnie, *r0kh_id, *subelem = NULL; size_t mdie_len, ftie_len, rsnie_len, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; struct rsn_ftie *_ftie; + struct wpa_ft_ies parse; + u8 *ric_start; if (sm == NULL) return pos; @@ -549,33 +680,27 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, _ftie = (struct rsn_ftie *) (ftie + 2); _ftie->mic_control[1] = 3; /* Information element count */ + + 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); + _ftie->mic_control[1] += ieee802_11_ie_count(ric_start, + pos - ric_start); + } + if (ric_start == pos) + ric_start = NULL; + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, - rsnie, rsnie_len, NULL, 0, _ftie->mic) < 0) + rsnie, rsnie_len, + ric_start, ric_start ? pos - ric_start : 0, + _ftie->mic) < 0) wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return pos; } -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *ric; - size_t ric_len; -}; - - static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, struct wpa_ft_ies *parse) { diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 67c704d7a..e04b28592 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -116,9 +116,13 @@ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 #define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 #define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +#define WLAN_STATUS_R0KH_UNREACHABLE 28 /* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 +#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_REQUEST_DECLINED 37 +#define WLAN_STATUS_INVALID_PARAMETERS 38 /* IEEE 802.11i */ #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 2fcb9f2ee..2853156c3 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -282,6 +282,12 @@ struct rsn_ftie { #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 +struct rsn_rdie { + u8 id; + u8 descr_count; + le16 status_code; +} STRUCT_PACKED; + #endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 014261c78..aa77a3d12 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -300,6 +300,8 @@ struct wpa_ft_ies { size_t tie_len; const u8 *igtk; size_t igtk_len; + const u8 *ric; + size_t ric_len; }; @@ -358,6 +360,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) @@ -386,6 +390,10 @@ 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; @@ -393,11 +401,55 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->tie = pos + 2; parse->tie_len = pos[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 (parse->tie) + 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; } @@ -818,16 +870,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, if (parse.tie) count++; - if (ftie->mic_control[1] != count) { - wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", - ftie->mic_control[1]); - return -1; - } - if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, 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 -1; @@ -848,6 +895,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; #endif /* CONFIG_IEEE80211W */ + if (parse.ric) { + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", + parse.ric, parse.ric_len); + /* TODO: parse response and inform driver about results */ + } + return 0; }