#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"
#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)
{
}
+#ifdef CONFIG_IEEE80211W
+static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (wpa_auth->cb.get_seqnum_igtk == NULL)
+ return -1;
+ return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq);
+}
+#endif /* CONFIG_IEEE80211W */
+
+
static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem;
}
+#ifdef CONFIG_IEEE80211W
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem, *pos;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len;
+
+ /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+ * Key[16+8] */
+ subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ pos = subelem;
+ *pos++ = FTIE_SUBELEM_IGTK;
+ *pos++ = subelem_len - 2;
+ WPA_PUT_LE16(pos, gsm->GN_igtk);
+ pos += 2;
+ wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+ pos += 6;
+ *pos++ = WPA_IGTK_LEN;
+ if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+ gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ os_free(subelem);
+ return NULL;
+ }
+
+ *len = subelem_len;
+ return subelem;
+}
+#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;
subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
r0kh_id = sm->r0kh_id;
r0kh_id_len = sm->r0kh_id_len;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_frame_prot) {
+ u8 *igtk;
+ size_t igtk_len;
+ u8 *nbuf;
+ igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+ if (igtk == NULL) {
+ os_free(subelem);
+ return pos;
+ }
+ nbuf = os_realloc(subelem, subelem_len + igtk_len);
+ if (nbuf == NULL) {
+ os_free(subelem);
+ os_free(igtk);
+ return pos;
+ }
+ subelem = nbuf;
+ os_memcpy(subelem + subelem_len, igtk, igtk_len);
+ subelem_len += igtk_len;
+ os_free(igtk);
+ }
+#endif /* CONFIG_IEEE80211W */
} else {
r0kh_id = conf->r0_key_holder;
r0kh_id_len = conf->r0_key_holder_len;
_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;
-};
-
-
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
struct wpa_ft_ies *parse)
{
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)
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;
}
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;
MAC2STR(frame->ap_address));
if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
- /* Discard frame per IEEE 802.11r/D8.0, 10A.10.3 */
+ /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
"unrecognized type %d", frame->frame_type);
return -1;