]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Decrypt Association Request elements and check Key-Auth (AP)
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 8 Sep 2015 22:27:22 +0000 (01:27 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 25 Oct 2016 17:42:52 +0000 (20:42 +0300)
Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/ap/ieee802_11.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h

index b47b659b4436099e60aa38154db9987b6be4fca1..399b175003043168656bd33b02cbf064d5df7b1a 100644 (file)
@@ -2366,6 +2366,7 @@ static void handle_assoc(struct hostapd_data *hapd,
        const u8 *pos;
        int left, i;
        struct sta_info *sta;
+       u8 *tmp = NULL;
 
        if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
                                      sizeof(mgmt->u.assoc_req))) {
@@ -2491,6 +2492,30 @@ static void handle_assoc(struct hostapd_data *hapd,
         */
        sta->capability = capab_info;
 
+#ifdef CONFIG_FILS
+       if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+           sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+           sta->auth_alg == WLAN_AUTH_FILS_PK) {
+               /* The end of the payload is encrypted. Need to decrypt it
+                * before parsing. */
+
+               tmp = os_malloc(left);
+               if (!tmp) {
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               os_memcpy(tmp, pos, left);
+
+               left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+                                         len, tmp, left);
+               if (left < 0) {
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               pos = tmp;
+       }
+#endif /* CONFIG_FILS */
+
        /* followed by SSID and Supported rates; and HT capabilities if 802.11n
         * is used */
        resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2600,6 +2625,7 @@ static void handle_assoc(struct hostapd_data *hapd,
                resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 
        reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+       os_free(tmp);
 
        /*
         * Remove the station in case tranmission of a success response fails
index a64e927aabbe06c4bf07951bacd404fd932773ee..b348818332bb041b5d66df87791d0cd0fb0805a5 100644 (file)
@@ -1747,6 +1747,10 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
        if (sm->mgmt_frame_prot && event == WPA_AUTH)
                remove_ptk = 0;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+       if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && event == WPA_AUTH)
+               remove_ptk = 0;
+#endif /* CONFIG_FILS */
 
        if (remove_ptk) {
                sm->PTK_valid = FALSE;
@@ -2057,6 +2061,11 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
                               sm->fils_key_auth_ap,
                               &sm->fils_key_auth_len);
        os_memset(ick, 0, sizeof(ick));
+
+       /* Store nonces for (Re)Association Request/Response frame processing */
+       os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+       os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
        return res;
 }
 
@@ -2114,6 +2123,138 @@ static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
                *_key_data_len = key_data_len;
        return 0;
 }
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+                      const struct ieee80211_mgmt *mgmt, size_t frame_len,
+                      u8 *pos, size_t left)
+{
+       u16 fc, stype;
+       const u8 *end, *ie_start, *ie, *session, *crypt;
+       struct ieee802_11_elems elems;
+       const u8 *aad[5];
+       size_t aad_len[5];
+
+       if (!sm || !sm->PTK_valid) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: No KEK to decrypt Assocication Request frame");
+               return -1;
+       }
+
+       if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Not a FILS AKM - reject association");
+               return -1;
+       }
+
+       end = ((const u8 *) mgmt) + frame_len;
+       fc = le_to_host16(mgmt->frame_control);
+       stype = WLAN_FC_GET_STYPE(fc);
+       if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+               ie_start = mgmt->u.reassoc_req.variable;
+       else
+               ie_start = mgmt->u.assoc_req.variable;
+       ie = ie_start;
+
+       /*
+        * Find FILS Session element which is the last unencrypted element in
+        * the frame.
+        */
+       session = NULL;
+       while (ie + 1 < end) {
+               if (ie + 2 + ie[1] > end)
+                       break;
+               if (ie[0] == WLAN_EID_EXTENSION &&
+                   ie[1] >= 1 + FILS_SESSION_LEN &&
+                   ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+                       session = ie;
+                       break;
+               }
+               ie += 2 + ie[1];
+       }
+
+       if (!session) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Could not find FILS Session element in Association Request frame - reject");
+               return -1;
+       }
+       if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+               wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+                           fils_session, FILS_SESSION_LEN);
+               wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+                           session + 3, FILS_SESSION_LEN);
+               return -1;
+       }
+       crypt = session + 2 + session[1];
+
+       if (end - crypt < AES_BLOCK_SIZE) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Too short frame to include AES-SIV data");
+               return -1;
+       }
+
+       /* AES-SIV AAD vectors */
+
+       /* The STA's MAC address */
+       aad[0] = mgmt->sa;
+       aad_len[0] = ETH_ALEN;
+       /* The AP's BSSID */
+       aad[1] = mgmt->da;
+       aad_len[1] = ETH_ALEN;
+       /* The STA's nonce */
+       aad[2] = sm->SNonce;
+       aad_len[2] = FILS_NONCE_LEN;
+       /* The AP's nonce */
+       aad[3] = sm->ANonce;
+       aad_len[3] = FILS_NONCE_LEN;
+       /*
+        * The (Re)Association Request frame from the Capability Information
+        * field to the FILS Session element (both inclusive).
+        */
+       aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+       aad_len[4] = crypt - aad[0];
+
+       if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+                           1, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Invalid AES-SIV data in the frame");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+                   pos, left - AES_BLOCK_SIZE);
+
+       if (ieee802_11_parse_elems(pos, left - AES_BLOCK_SIZE, &elems, 1) ==
+           ParseFailed) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Failed to parse decrypted elements");
+               return -1;
+       }
+       if (!elems.fils_key_confirm) {
+               wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+               return -1;
+       }
+       if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Unexpected Key-Auth length %d (expected %d)",
+                          elems.fils_key_confirm_len,
+                          (int) sm->fils_key_auth_len);
+               return -1;
+       }
+       if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+                     sm->fils_key_auth_len) != 0) {
+               wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+               wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+                           elems.fils_key_confirm,
+                           elems.fils_key_confirm_len);
+               wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+                           sm->fils_key_auth_sta, sm->fils_key_auth_len);
+               return -1;
+       }
+
+       return left - AES_BLOCK_SIZE;
+}
+
 #endif /* CONFIG_FILS */
 
 
index 5ab780276dbf96e3cd5455e30aa31dd1fa59eb76..273e290890de2a3140fa811dc40d44022420908f 100644 (file)
@@ -350,5 +350,8 @@ int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
                         size_t pmk_len, const u8 *snonce, const u8 *anonce);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+                      const struct ieee80211_mgmt *mgmt, size_t frame_len,
+                      u8 *pos, size_t left);
 
 #endif /* WPA_AUTH_H */