#include "includes.h"
#include "common.h"
+#include "crypto/aes_wrap.h"
#include "config.h"
#include "wpa.h"
-#include "aes_wrap.h"
#include "ieee802_11.h"
-#include "defs.h"
+#include "wme.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)
{
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk)
+ struct wpa_ptk *ptk, size_t ptk_len)
{
u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
sm->wpa_auth->addr, pmk_r1_name,
- (u8 *) ptk, sizeof(*ptk), ptk_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk));
+ (u8 *) ptk, ptk_len, ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
return 0;
}
+#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(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;
+ }
+
+#ifdef NEED_AP_MLME
+ 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;
+ }
+#endif /* NEED_AP_MLME */
+
+ 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;
}
static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
int vlan_id,
- const char *alg, const u8 *addr, int idx,
+ wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len)
{
if (wpa_auth->cb.set_key == NULL)
static void wpa_ft_install_ptk(struct wpa_state_machine *sm)
{
- char *alg;
+ wpa_alg alg;
int klen;
/* MLME-SETKEYS.request(PTK) */
if (sm->pairwise == WPA_CIPHER_TKIP) {
- alg = "TKIP";
+ alg = WPA_ALG_TKIP;
klen = 32;
} else if (sm->pairwise == WPA_CIPHER_CCMP) {
- alg = "CCMP";
+ alg = WPA_ALG_CCMP;
klen = 16;
} else
return;
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
- size_t buflen;
+ size_t buflen, ptk_len;
int ret;
u8 *pos, *end;
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
+ ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
sm->wpa_auth->addr, pmk_r1_name,
- (u8 *) &sm->PTK, sizeof(sm->PTK), ptk_name);
+ (u8 *) &sm->PTK, ptk_len, ptk_name);
wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
- (u8 *) &sm->PTK, sizeof(sm->PTK));
+ (u8 *) &sm->PTK, ptk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
wpa_ft_install_ptk(sm);
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;