@ -1055,6 +1055,228 @@ static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
}
static void process_gtk_subelem ( struct wlantest * wt , struct wlantest_bss * bss ,
struct wlantest_sta * sta ,
const u8 * kek , size_t kek_len ,
const u8 * gtk_elem ,
size_t gtk_elem_len )
{
u8 gtk [ 32 ] ;
int keyidx ;
enum wpa_alg alg ;
size_t gtk_len , keylen ;
const u8 * rsc ;
if ( ! gtk_elem ) {
add_note ( wt , MSG_INFO , " FT: No GTK included in FTE " ) ;
return ;
}
wpa_hexdump ( MSG_DEBUG , " FT: Received GTK in Reassoc Resp " ,
gtk_elem , gtk_elem_len ) ;
if ( gtk_elem_len < 11 + 24 | | ( gtk_elem_len - 11 ) % 8 | |
gtk_elem_len - 19 > sizeof ( gtk ) ) {
add_note ( wt , MSG_INFO , " FT: Invalid GTK sub-elem length %zu " ,
gtk_elem_len ) ;
return ;
}
gtk_len = gtk_elem_len - 19 ;
if ( aes_unwrap ( kek , kek_len , gtk_len / 8 , gtk_elem + 11 , gtk ) ) {
add_note ( wt , MSG_INFO ,
" FT: AES unwrap failed - could not decrypt GTK " ) ;
return ;
}
keylen = wpa_cipher_key_len ( bss - > group_cipher ) ;
alg = wpa_cipher_to_alg ( bss - > group_cipher ) ;
if ( alg = = WPA_ALG_NONE ) {
add_note ( wt , MSG_INFO , " FT: Unsupported Group Cipher %d " ,
bss - > group_cipher ) ;
return ;
}
if ( gtk_len < keylen ) {
add_note ( wt , MSG_INFO , " FT: Too short GTK in FTE " ) ;
return ;
}
/* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
keyidx = WPA_GET_LE16 ( gtk_elem ) & 0x03 ;
if ( gtk_elem [ 2 ] ! = keylen ) {
add_note ( wt , MSG_INFO ,
" FT: GTK length mismatch: received %u negotiated %zu " ,
gtk_elem [ 2 ] , keylen ) ;
return ;
}
add_note ( wt , MSG_DEBUG , " GTK KeyID=%u " , keyidx ) ;
wpa_hexdump ( MSG_DEBUG , " FT: GTK from Reassoc Resp " , gtk , keylen ) ;
if ( bss - > group_cipher = = WPA_CIPHER_TKIP ) {
/* Swap Tx/Rx keys for Michael MIC */
u8 tmp [ 8 ] ;
os_memcpy ( tmp , gtk + 16 , 8 ) ;
os_memcpy ( gtk + 16 , gtk + 24 , 8 ) ;
os_memcpy ( gtk + 24 , tmp , 8 ) ;
}
bss - > gtk_len [ keyidx ] = gtk_len ;
sta - > gtk_len = gtk_len ;
os_memcpy ( bss - > gtk [ keyidx ] , gtk , gtk_len ) ;
os_memcpy ( sta - > gtk , gtk , gtk_len ) ;
rsc = gtk_elem + 2 ;
bss - > rsc [ keyidx ] [ 0 ] = rsc [ 5 ] ;
bss - > rsc [ keyidx ] [ 1 ] = rsc [ 4 ] ;
bss - > rsc [ keyidx ] [ 2 ] = rsc [ 3 ] ;
bss - > rsc [ keyidx ] [ 3 ] = rsc [ 2 ] ;
bss - > rsc [ keyidx ] [ 4 ] = rsc [ 1 ] ;
bss - > rsc [ keyidx ] [ 5 ] = rsc [ 0 ] ;
bss - > gtk_idx = keyidx ;
sta - > gtk_idx = keyidx ;
wpa_hexdump ( MSG_DEBUG , " RSC " , bss - > rsc [ keyidx ] , 6 ) ;
}
static void process_igtk_subelem ( struct wlantest * wt , struct wlantest_bss * bss ,
struct wlantest_sta * sta ,
const u8 * kek , size_t kek_len ,
const u8 * igtk_elem , size_t igtk_elem_len )
{
u8 igtk [ WPA_IGTK_MAX_LEN ] ;
size_t igtk_len ;
u16 keyidx ;
const u8 * ipn ;
if ( bss - > mgmt_group_cipher ! = WPA_CIPHER_AES_128_CMAC & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_GMAC_128 & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_GMAC_256 & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_CMAC_256 )
return ;
if ( ! igtk_elem ) {
add_note ( wt , MSG_INFO , " FT: No IGTK included in FTE " ) ;
return ;
}
wpa_hexdump ( MSG_DEBUG , " FT: Received IGTK in Reassoc Resp " ,
igtk_elem , igtk_elem_len ) ;
igtk_len = wpa_cipher_key_len ( bss - > mgmt_group_cipher ) ;
if ( igtk_elem_len ! = 2 + 6 + 1 + igtk_len + 8 ) {
add_note ( wt , MSG_INFO , " FT: Invalid IGTK sub-elem length %zu " ,
igtk_elem_len ) ;
return ;
}
if ( igtk_elem [ 8 ] ! = igtk_len ) {
add_note ( wt , MSG_INFO ,
" FT: Invalid IGTK sub-elem Key Length %d " ,
igtk_elem [ 8 ] ) ;
return ;
}
if ( aes_unwrap ( kek , kek_len , igtk_len / 8 , igtk_elem + 9 , igtk ) ) {
add_note ( wt , MSG_INFO ,
" FT: AES unwrap failed - could not decrypt IGTK " ) ;
return ;
}
/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
keyidx = WPA_GET_LE16 ( igtk_elem ) ;
wpa_hexdump ( MSG_DEBUG , " FT: IGTK from Reassoc Resp " , igtk , igtk_len ) ;
if ( keyidx < 4 | | keyidx > 5 ) {
add_note ( wt , MSG_INFO , " Unexpected IGTK KeyID %u " , keyidx ) ;
return ;
}
add_note ( wt , MSG_DEBUG , " IGTK KeyID %u " , keyidx ) ;
wpa_hexdump ( MSG_DEBUG , " IPN " , igtk_elem + 2 , 6 ) ;
wpa_hexdump ( MSG_DEBUG , " IGTK " , igtk , igtk_len ) ;
os_memcpy ( bss - > igtk [ keyidx ] , igtk , igtk_len ) ;
bss - > igtk_len [ keyidx ] = igtk_len ;
ipn = igtk_elem + 2 ;
bss - > ipn [ keyidx ] [ 0 ] = ipn [ 5 ] ;
bss - > ipn [ keyidx ] [ 1 ] = ipn [ 4 ] ;
bss - > ipn [ keyidx ] [ 2 ] = ipn [ 3 ] ;
bss - > ipn [ keyidx ] [ 3 ] = ipn [ 2 ] ;
bss - > ipn [ keyidx ] [ 4 ] = ipn [ 1 ] ;
bss - > ipn [ keyidx ] [ 5 ] = ipn [ 0 ] ;
bss - > igtk_idx = keyidx ;
}
static void process_bigtk_subelem ( struct wlantest * wt , struct wlantest_bss * bss ,
struct wlantest_sta * sta ,
const u8 * kek , size_t kek_len ,
const u8 * bigtk_elem , size_t bigtk_elem_len )
{
u8 bigtk [ WPA_BIGTK_MAX_LEN ] ;
size_t bigtk_len ;
u16 keyidx ;
const u8 * ipn ;
if ( ! bigtk_elem | |
( bss - > mgmt_group_cipher ! = WPA_CIPHER_AES_128_CMAC & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_GMAC_128 & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_GMAC_256 & &
bss - > mgmt_group_cipher ! = WPA_CIPHER_BIP_CMAC_256 ) )
return ;
wpa_hexdump_key ( MSG_DEBUG , " FT: Received BIGTK in Reassoc Resp " ,
bigtk_elem , bigtk_elem_len ) ;
bigtk_len = wpa_cipher_key_len ( bss - > mgmt_group_cipher ) ;
if ( bigtk_elem_len ! = 2 + 6 + 1 + bigtk_len + 8 ) {
add_note ( wt , MSG_INFO ,
" FT: Invalid BIGTK sub-elem length %zu " ,
bigtk_elem_len ) ;
return ;
}
if ( bigtk_elem [ 8 ] ! = bigtk_len ) {
add_note ( wt , MSG_INFO ,
" FT: Invalid BIGTK sub-elem Key Length %d " ,
bigtk_elem [ 8 ] ) ;
return ;
}
if ( aes_unwrap ( kek , kek_len , bigtk_len / 8 , bigtk_elem + 9 , bigtk ) ) {
add_note ( wt , MSG_INFO ,
" FT: AES unwrap failed - could not decrypt BIGTK " ) ;
return ;
}
/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
keyidx = WPA_GET_LE16 ( bigtk_elem ) ;
wpa_hexdump ( MSG_DEBUG , " FT: BIGTK from Reassoc Resp " , bigtk , bigtk_len ) ;
if ( keyidx < 6 | | keyidx > 7 ) {
add_note ( wt , MSG_INFO , " Unexpected BIGTK KeyID %u " , keyidx ) ;
return ;
}
add_note ( wt , MSG_DEBUG , " BIGTK KeyID %u " , keyidx ) ;
wpa_hexdump ( MSG_DEBUG , " BIPN " , bigtk_elem + 2 , 6 ) ;
wpa_hexdump ( MSG_DEBUG , " BIGTK " , bigtk , bigtk_len ) ;
os_memcpy ( bss - > igtk [ keyidx ] , bigtk , bigtk_len ) ;
bss - > igtk_len [ keyidx ] = bigtk_len ;
ipn = bigtk_elem + 2 ;
bss - > ipn [ keyidx ] [ 0 ] = ipn [ 5 ] ;
bss - > ipn [ keyidx ] [ 1 ] = ipn [ 4 ] ;
bss - > ipn [ keyidx ] [ 2 ] = ipn [ 3 ] ;
bss - > ipn [ keyidx ] [ 3 ] = ipn [ 2 ] ;
bss - > ipn [ keyidx ] [ 4 ] = ipn [ 1 ] ;
bss - > ipn [ keyidx ] [ 5 ] = ipn [ 0 ] ;
bss - > bigtk_idx = keyidx ;
}
static void rx_mgmt_reassoc_resp ( struct wlantest * wt , const u8 * data ,
size_t len )
{
@ -1064,6 +1286,7 @@ static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
u16 capab , status , aid ;
const u8 * ies ;
size_t ies_len ;
struct ieee802_11_elems elems ;
mgmt = ( const struct ieee80211_mgmt * ) data ;
bss = bss_get ( wt , mgmt - > bssid ) ;
@ -1106,17 +1329,15 @@ static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
}
}
if ( status = = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY ) {
struct ieee802_11_elems elems ;
if ( ieee802_11_parse_elems ( ies , ies_len , & elems , 0 ) = = ParseFailed ) {
add_note ( wt , MSG_INFO ,
" Failed to parse IEs in ReassocResp from " MACSTR ,
MAC2STR ( mgmt - > sa ) ) ;
}
if ( ieee802_11_parse_elems ( ies , ies_len , & elems , 0 ) = =
ParseFailed ) {
add_note ( wt , MSG_INFO , " Failed to parse IEs in "
" ReassocResp from " MACSTR ,
MAC2STR ( mgmt - > sa ) ) ;
} else if ( elems . timeout_int = = NULL | |
elems . timeout_int [ 0 ] ! =
WLAN_TIMEOUT_ASSOC_COMEBACK ) {
if ( status = = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY ) {
if ( ! elems . timeout_int | |
elems . timeout_int [ 0 ] ! = WLAN_TIMEOUT_ASSOC_COMEBACK ) {
add_note ( wt , MSG_INFO , " No valid Timeout Interval IE "
" with Assoc Comeback time in ReassocResp "
" (status=30) from " MACSTR ,
@ -1149,6 +1370,212 @@ static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
MAC2STR ( sta - > addr ) , MAC2STR ( bss - > bssid ) ) ;
sta - > state = STATE3 ;
}
if ( elems . ftie ) {
struct wpa_ft_ies parse ;
int use_sha384 ;
struct rsn_mdie * mde ;
const u8 * anonce , * snonce , * fte_mic ;
u8 fte_elem_count ;
unsigned int count ;
u8 mic [ WPA_EAPOL_KEY_MIC_MAX_LEN ] ;
size_t mic_len = 16 ;
const u8 * kck , * kek ;
size_t kck_len , kek_len ;
use_sha384 = wpa_key_mgmt_sha384 ( sta - > key_mgmt ) ;
if ( wpa_ft_parse_ies ( ies , ies_len , & parse , use_sha384 ) < 0 ) {
add_note ( wt , MSG_INFO , " FT: Failed to parse FT IEs " ) ;
return ;
}
if ( ! parse . rsn ) {
add_note ( wt , MSG_INFO , " FT: No RSNE in Reassoc Resp " ) ;
return ;
}
if ( ! parse . rsn_pmkid ) {
add_note ( wt , MSG_INFO , " FT: No PMKID in RSNE " ) ;
return ;
}
if ( os_memcmp_const ( parse . rsn_pmkid , sta - > pmk_r1_name ,
WPA_PMK_NAME_LEN ) ! = 0 ) {
add_note ( wt , MSG_INFO ,
" FT: PMKID in Reassoc Resp did not match PMKR1Name " ) ;
wpa_hexdump ( MSG_DEBUG ,
" FT: Received RSNE[PMKR1Name] " ,
parse . rsn_pmkid , WPA_PMK_NAME_LEN ) ;
wpa_hexdump ( MSG_DEBUG ,
" FT: Previously derived PMKR1Name " ,
sta - > pmk_r1_name , WPA_PMK_NAME_LEN ) ;
return ;
}
mde = ( struct rsn_mdie * ) parse . mdie ;
if ( ! mde | | parse . mdie_len < sizeof ( * mde ) | |
os_memcmp ( mde - > mobility_domain , bss - > mdid ,
MOBILITY_DOMAIN_ID_LEN ) ! = 0 ) {
add_note ( wt , MSG_INFO , " FT: Invalid MDE " ) ;
}
if ( use_sha384 ) {
struct rsn_ftie_sha384 * fte ;
fte = ( struct rsn_ftie_sha384 * ) parse . ftie ;
if ( ! fte | | parse . ftie_len < sizeof ( * fte ) ) {
add_note ( wt , MSG_INFO , " FT: Invalid FTE " ) ;
return ;
}
anonce = fte - > anonce ;
snonce = fte - > snonce ;
fte_elem_count = fte - > mic_control [ 1 ] ;
fte_mic = fte - > mic ;
} else {
struct rsn_ftie * fte ;
fte = ( struct rsn_ftie * ) parse . ftie ;
if ( ! fte | | parse . ftie_len < sizeof ( * fte ) ) {
add_note ( wt , MSG_INFO , " FT: Invalid FTIE " ) ;
return ;
}
anonce = fte - > anonce ;
snonce = fte - > snonce ;
fte_elem_count = fte - > mic_control [ 1 ] ;
fte_mic = fte - > mic ;
}
if ( os_memcmp ( snonce , sta - > snonce , WPA_NONCE_LEN ) ! = 0 ) {
add_note ( wt , MSG_INFO , " FT: SNonce mismatch in FTIE " ) ;
wpa_hexdump ( MSG_DEBUG , " FT: Received SNonce " ,
snonce , WPA_NONCE_LEN ) ;
wpa_hexdump ( MSG_DEBUG , " FT: Expected SNonce " ,
sta - > snonce , WPA_NONCE_LEN ) ;
return ;
}
if ( os_memcmp ( anonce , sta - > anonce , WPA_NONCE_LEN ) ! = 0 ) {
add_note ( wt , MSG_INFO , " FT: ANonce mismatch in FTIE " ) ;
wpa_hexdump ( MSG_DEBUG , " FT: Received ANonce " ,
anonce , WPA_NONCE_LEN ) ;
wpa_hexdump ( MSG_DEBUG , " FT: Expected ANonce " ,
sta - > anonce , WPA_NONCE_LEN ) ;
return ;
}
if ( ! parse . r0kh_id ) {
add_note ( wt , MSG_INFO , " FT: No R0KH-ID subelem in FTE " ) ;
return ;
}
if ( parse . r0kh_id_len ! = bss - > r0kh_id_len | |
os_memcmp_const ( parse . r0kh_id , bss - > r0kh_id ,
parse . r0kh_id_len ) ! = 0 ) {
add_note ( wt , MSG_INFO ,
" FT: R0KH-ID in FTE did not match the current R0KH-ID " ) ;
wpa_hexdump ( MSG_DEBUG , " FT: R0KH-ID in FTIE " ,
parse . r0kh_id , parse . r0kh_id_len ) ;
wpa_hexdump ( MSG_DEBUG , " FT: The current R0KH-ID " ,
bss - > r0kh_id , bss - > r0kh_id_len ) ;
os_memcpy ( bss - > r0kh_id , parse . r0kh_id ,
parse . r0kh_id_len ) ;
bss - > r0kh_id_len = parse . r0kh_id_len ;
}
if ( ! parse . r1kh_id ) {
add_note ( wt , MSG_INFO , " FT: No R1KH-ID subelem in FTE " ) ;
return ;
}
if ( os_memcmp_const ( parse . r1kh_id , bss - > r1kh_id ,
FT_R1KH_ID_LEN ) ! = 0 ) {
add_note ( wt , MSG_INFO ,
" FT: Unknown R1KH-ID used in ReassocResp " ) ;
os_memcpy ( bss - > r1kh_id , parse . r1kh_id , FT_R1KH_ID_LEN ) ;
}
count = 3 ;
if ( parse . ric )
count + = ieee802_11_ie_count ( parse . ric , parse . ric_len ) ;
if ( parse . rsnxe )
count + + ;
if ( fte_elem_count ! = count ) {
add_note ( wt , MSG_INFO ,
" FT: Unexpected IE count in MIC Control: received %u expected %u " ,
fte_elem_count , count ) ;
return ;
}
if ( wpa_key_mgmt_fils ( sta - > key_mgmt ) ) {
kck = sta - > ptk . kck2 ;
kck_len = sta - > ptk . kck2_len ;
kek = sta - > ptk . kek2 ;
kek_len = sta - > ptk . kek2_len ;
} else {
kck = sta - > ptk . kck ;
kck_len = sta - > ptk . kck_len ;
kek = sta - > ptk . kek ;
kek_len = sta - > ptk . kek_len ;
}
if ( wpa_ft_mic ( kck , kck_len , sta - > addr , bss - > bssid , 6 ,
parse . mdie - 2 , parse . mdie_len + 2 ,
parse . ftie - 2 , parse . ftie_len + 2 ,
parse . rsn - 2 , parse . rsn_len + 2 ,
parse . ric , parse . ric_len ,
parse . rsnxe ? parse . rsnxe - 2 : NULL ,
parse . rsnxe ? parse . rsnxe_len + 2 : 0 ,
mic ) < 0 ) {
add_note ( wt , MSG_INFO , " FT: Failed to calculate MIC " ) ;
return ;
}
if ( os_memcmp_const ( mic , fte_mic , mic_len ) ! = 0 ) {
add_note ( wt , MSG_INFO , " FT: Invalid MIC in FTE " ) ;
wpa_printf ( MSG_DEBUG ,
" FT: addr= " MACSTR " auth_addr= " MACSTR ,
MAC2STR ( sta - > addr ) ,
MAC2STR ( bss - > bssid ) ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: Received MIC " ,
fte_mic , mic_len ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: Calculated MIC " ,
mic , mic_len ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: MDE " ,
parse . mdie - 2 , parse . mdie_len + 2 ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: FTE " ,
parse . ftie - 2 , parse . ftie_len + 2 ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: RSN " ,
parse . rsn - 2 , parse . rsn_len + 2 ) ;
wpa_hexdump ( MSG_MSGDUMP , " FT: RSNXE " ,
parse . rsnxe ? parse . rsnxe - 2 : NULL ,
parse . rsnxe ? parse . rsnxe_len + 2 : 0 ) ;
return ;
}
add_note ( wt , MSG_INFO , " FT: Valid FTE MIC " ) ;
if ( wpa_compare_rsn_ie ( wpa_key_mgmt_ft ( sta - > key_mgmt ) ,
bss - > rsnie , 2 + bss - > rsnie [ 1 ] ,
parse . rsn - 2 , parse . rsn_len + 2 ) ) {
add_note ( wt , MSG_INFO ,
" FT: RSNE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame " ) ;
wpa_hexdump ( MSG_INFO , " RSNE in Beacon/ProbeResp " ,
& bss - > rsnie [ 2 ] , bss - > rsnie [ 1 ] ) ;
wpa_hexdump ( MSG_INFO ,
" RSNE in FT protocol Reassociation Response frame " ,
parse . rsn ? parse . rsn - 2 : NULL ,
parse . rsn ? parse . rsn_len + 2 : 0 ) ;
}
process_gtk_subelem ( wt , bss , sta , kek , kek_len ,
parse . gtk , parse . gtk_len ) ;
process_igtk_subelem ( wt , bss , sta , kek , kek_len ,
parse . igtk , parse . igtk_len ) ;
process_bigtk_subelem ( wt , bss , sta , kek , kek_len ,
parse . bigtk , parse . bigtk_len ) ;
}
}