FILS: Decrypt Association Request elements and check Key-Auth (AP)
Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
86cd6928e0
commit
78815f3dde
3 changed files with 170 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue