Replace hostapd_michael_mic_failure() with generic driver event
[libeap.git] / hostapd / wpa_ft.c
index 44249e2..65d673a 100644 (file)
 #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)
 {
@@ -321,7 +343,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
 
 
 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];
@@ -354,8 +376,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 
        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;
@@ -426,14 +448,158 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
 }
 
 
+#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;
@@ -467,6 +633,28 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *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;
@@ -482,31 +670,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;
-};
-
-
 static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
                             struct wpa_ft_ies *parse)
 {
@@ -556,6 +740,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)
@@ -584,21 +770,67 @@ 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;
 }
 
 
 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)
@@ -610,15 +842,15 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 
 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;
@@ -647,7 +879,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
        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;
 
@@ -737,11 +969,12 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
        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);
@@ -870,20 +1103,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;
@@ -1274,7 +1498,7 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
                   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;