FILS: Decrypt Association Request elements and check Key-Auth (AP)

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2015-09-09 01:27:22 +03:00 committed by Jouni Malinen
parent 86cd6928e0
commit 78815f3dde
3 changed files with 170 additions and 0 deletions

View File

@ -2366,6 +2366,7 @@ static void handle_assoc(struct hostapd_data *hapd,
const u8 *pos; const u8 *pos;
int left, i; int left, i;
struct sta_info *sta; struct sta_info *sta;
u8 *tmp = NULL;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) { sizeof(mgmt->u.assoc_req))) {
@ -2491,6 +2492,30 @@ static void handle_assoc(struct hostapd_data *hapd,
*/ */
sta->capability = capab_info; 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 /* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */ * is used */
resp = check_assoc_ies(hapd, sta, pos, left, reassoc); 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; resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); 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 * Remove the station in case tranmission of a success response fails

View 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) if (sm->mgmt_frame_prot && event == WPA_AUTH)
remove_ptk = 0; remove_ptk = 0;
#endif /* CONFIG_IEEE80211W */ #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) { if (remove_ptk) {
sm->PTK_valid = FALSE; 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_ap,
&sm->fils_key_auth_len); &sm->fils_key_auth_len);
os_memset(ick, 0, sizeof(ick)); 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; 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; *_key_data_len = key_data_len;
return 0; 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 */ #endif /* CONFIG_FILS */

View 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 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, int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
size_t pmk_len, const u8 *snonce, const u8 *anonce); 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 */ #endif /* WPA_AUTH_H */