@ -647,14 +647,14 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
}
static int wpa_replay_counter_valid ( struct wpa_ state_machine * sm ,
static int wpa_replay_counter_valid ( struct wpa_ key_replay_counter * ctr ,
const u8 * replay_counter )
{
int i ;
for ( i = 0 ; i < RSNA_MAX_EAPOL_RETRIES ; i + + ) {
if ( ! sm- > key_replay [ i ] . valid )
if ( ! ctr [ i ] . valid )
break ;
if ( os_memcmp ( replay_counter , sm- > key_replay [ i ] . counter ,
if ( os_memcmp ( replay_counter , ctr [ i ] . counter ,
WPA_REPLAY_COUNTER_LEN ) = = 0 )
return 1 ;
}
@ -662,6 +662,20 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
}
static void wpa_replay_counter_mark_invalid ( struct wpa_key_replay_counter * ctr ,
const u8 * replay_counter )
{
int i ;
for ( i = 0 ; i < RSNA_MAX_EAPOL_RETRIES ; i + + ) {
if ( ctr [ i ] . valid & &
( replay_counter = = NULL | |
os_memcmp ( replay_counter , ctr [ i ] . counter ,
WPA_REPLAY_COUNTER_LEN ) = = 0 ) )
ctr [ i ] . valid = FALSE ;
}
}
# ifdef CONFIG_IEEE80211R
static int ft_check_msg_2_of_4 ( struct wpa_authenticator * wpa_auth ,
struct wpa_state_machine * sm ,
@ -868,11 +882,44 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
if ( ! ( key_info & WPA_KEY_INFO_REQUEST ) & &
! wpa_replay_counter_valid ( sm , key - > replay_counter ) ) {
! wpa_replay_counter_valid ( sm - > key_replay , key - > replay_counter ) ) {
int i ;
wpa_auth_vlogger ( wpa_auth , sm - > addr , LOGGER_DEBUG ,
" received EAPOL-Key %s with unexpected "
" replay counter " , msgtxt ) ;
if ( msg = = PAIRWISE_2 & &
wpa_replay_counter_valid ( sm - > prev_key_replay ,
key - > replay_counter ) & &
sm - > wpa_ptk_state = = WPA_PTK_PTKINITNEGOTIATING & &
os_memcmp ( sm - > SNonce , key - > key_nonce , WPA_NONCE_LEN ) ! = 0 )
{
/*
* Some supplicant implementations ( e . g . , Windows XP
* WZC ) update SNonce for each EAPOL - Key 2 / 4. This
* breaks the workaround on accepting any of the
* pending requests , so allow the SNonce to be updated
* even if we have already sent out EAPOL - Key 3 / 4.
*/
wpa_auth_vlogger ( wpa_auth , sm - > addr , LOGGER_DEBUG ,
" Process SNonce update from STA "
" based on retransmitted EAPOL-Key "
" 1/4 " ) ;
sm - > update_snonce = 1 ;
wpa_replay_counter_mark_invalid ( sm - > prev_key_replay ,
key - > replay_counter ) ;
goto continue_processing ;
}
if ( msg = = PAIRWISE_2 & &
wpa_replay_counter_valid ( sm - > prev_key_replay ,
key - > replay_counter ) & &
sm - > wpa_ptk_state = = WPA_PTK_PTKINITNEGOTIATING ) {
wpa_auth_vlogger ( wpa_auth , sm - > addr , LOGGER_DEBUG ,
" ignore retransmitted EAPOL-Key %s - "
" SNonce did not change " , msgtxt ) ;
} else {
wpa_auth_vlogger ( wpa_auth , sm - > addr , LOGGER_DEBUG ,
" received EAPOL-Key %s with "
" unexpected replay counter " , msgtxt ) ;
}
for ( i = 0 ; i < RSNA_MAX_EAPOL_RETRIES ; i + + ) {
if ( ! sm - > key_replay [ i ] . valid )
break ;
@ -885,10 +932,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
return ;
}
continue_processing :
switch ( msg ) {
case PAIRWISE_2 :
if ( sm - > wpa_ptk_state ! = WPA_PTK_PTKSTART & &
sm - > wpa_ptk_state ! = WPA_PTK_PTKCALCNEGOTIATING ) {
sm - > wpa_ptk_state ! = WPA_PTK_PTKCALCNEGOTIATING & &
( ! sm - > update_snonce | |
sm - > wpa_ptk_state ! = WPA_PTK_PTKINITNEGOTIATING ) ) {
wpa_auth_vlogger ( wpa_auth , sm - > addr , LOGGER_INFO ,
" received EAPOL-Key msg 2/4 in "
" invalid state (%d) - dropped " ,
@ -1017,7 +1067,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
sm - > MICVerified = FALSE ;
if ( sm - > PTK_valid ) {
if ( sm - > PTK_valid & & ! sm - > update_snonce ) {
if ( wpa_verify_key_mic ( & sm - > PTK , data , data_len ) ) {
wpa_auth_logger ( wpa_auth , sm - > addr , LOGGER_INFO ,
" received EAPOL-Key with invalid MIC " ) ;
@ -1075,12 +1125,30 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_rekey_gtk ( wpa_auth , NULL ) ;
}
} else {
/* Do not allow the same key replay counter to be reused. This
* does also invalidate all other pending replay counters if
* retransmissions were used , i . e . , we will only process one of
* the pending replies and ignore rest if more than one is
* received . */
sm - > key_replay [ 0 ] . valid = FALSE ;
/* Do not allow the same key replay counter to be reused. */
wpa_replay_counter_mark_invalid ( sm - > key_replay ,
key - > replay_counter ) ;
if ( msg = = PAIRWISE_2 ) {
/*
* Maintain a copy of the pending EAPOL - Key frames in
* case the EAPOL - Key frame was retransmitted . This is
* needed to allow EAPOL - Key msg 2 / 4 reply to another
* pending msg 1 / 4 to update the SNonce to work around
* unexpected supplicant behavior .
*/
os_memcpy ( sm - > prev_key_replay , sm - > key_replay ,
sizeof ( sm - > key_replay ) ) ;
} else {
os_memset ( sm - > prev_key_replay , 0 ,
sizeof ( sm - > prev_key_replay ) ) ;
}
/*
* Make sure old valid counters are not accepted anymore and
* do not get copied again .
*/
wpa_replay_counter_mark_invalid ( sm - > key_replay , NULL ) ;
}
# ifdef CONFIG_PEERKEY
@ -1713,6 +1781,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
SM_ENTRY_MA ( WPA_PTK , PTKCALCNEGOTIATING , wpa_ptk ) ;
sm - > EAPOLKeyReceived = FALSE ;
sm - > update_snonce = FALSE ;
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA - PSK : iterate through possible PSKs and select the one matching
@ -2132,8 +2201,10 @@ SM_STEP(WPA_PTK)
SM_ENTER ( WPA_PTK , PTKINITNEGOTIATING ) ;
break ;
case WPA_PTK_PTKINITNEGOTIATING :
if ( sm - > EAPOLKeyReceived & & ! sm - > EAPOLKeyRequest & &
sm - > EAPOLKeyPairwise & & sm - > MICVerified )
if ( sm - > update_snonce )
SM_ENTER ( WPA_PTK , PTKCALCNEGOTIATING ) ;
else if ( sm - > EAPOLKeyReceived & & ! sm - > EAPOLKeyRequest & &
sm - > EAPOLKeyPairwise & & sm - > MICVerified )
SM_ENTER ( WPA_PTK , PTKINITDONE ) ;
else if ( sm - > TimeoutCtr >
( int ) dot11RSNAConfigPairwiseUpdateCount ) {