@ -15,6 +15,7 @@
# include "includes.h"
# include "common.h"
# include "crypto/sha256.h"
# include "eap_i.h"
# include "state_machine.h"
# include "common/wpa_ctrl.h"
@ -60,6 +61,27 @@ static const char * eap_get_erp_domain(struct eap_sm *sm)
}
# ifdef CONFIG_ERP
static struct eap_server_erp_key * eap_erp_get_key ( struct eap_sm * sm ,
const char * keyname )
{
if ( sm - > eapol_cb - > erp_get_key )
return sm - > eapol_cb - > erp_get_key ( sm - > eapol_ctx , keyname ) ;
return NULL ;
}
static int eap_erp_add_key ( struct eap_sm * sm , struct eap_server_erp_key * erp )
{
if ( sm - > eapol_cb - > erp_add_key )
return sm - > eapol_cb - > erp_add_key ( sm - > eapol_ctx , erp ) ;
return - 1 ;
}
# endif /* CONFIG_ERP */
static struct wpabuf * eap_sm_buildInitiateReauthStart ( struct eap_sm * sm ,
u8 id )
{
@ -71,7 +93,7 @@ static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
domain = eap_get_erp_domain ( sm ) ;
if ( domain ) {
domain_len = os_strlen ( domain ) ;
plen + = 2 + domain_len ; ;
plen + = 2 + domain_len ;
}
msg = eap_msg_alloc ( EAP_VENDOR_IETF , EAP_ERP_TYPE_REAUTH_START , plen ,
@ -210,7 +232,6 @@ SM_STATE(EAP, INITIALIZE)
eap_server_clear_identity ( sm ) ;
}
sm - > initiate_reauth_start_sent = FALSE ;
sm - > try_initiate_reauth = FALSE ;
sm - > currentId = - 1 ;
sm - > eap_if . eapSuccess = FALSE ;
@ -387,6 +408,95 @@ SM_STATE(EAP, METHOD_REQUEST)
}
static void eap_server_erp_init ( struct eap_sm * sm )
{
# ifdef CONFIG_ERP
u8 * emsk = NULL ;
size_t emsk_len ;
u8 EMSKname [ EAP_EMSK_NAME_LEN ] ;
u8 len [ 2 ] ;
const char * domain ;
size_t domain_len , nai_buf_len ;
struct eap_server_erp_key * erp = NULL ;
int pos ;
domain = eap_get_erp_domain ( sm ) ;
if ( ! domain )
return ;
domain_len = os_strlen ( domain ) ;
nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len ;
if ( nai_buf_len > 253 ) {
/*
* keyName - NAI has a maximum length of 253 octet to fit in
* RADIUS attributes .
*/
wpa_printf ( MSG_DEBUG ,
" EAP: Too long realm for ERP keyName-NAI maximum length " ) ;
return ;
}
nai_buf_len + + ; /* null termination */
erp = os_zalloc ( sizeof ( * erp ) + nai_buf_len ) ;
if ( erp = = NULL )
goto fail ;
erp - > recv_seq = ( u32 ) - 1 ;
emsk = sm - > m - > get_emsk ( sm , sm - > eap_method_priv , & emsk_len ) ;
if ( ! emsk | | emsk_len = = 0 | | emsk_len > ERP_MAX_KEY_LEN ) {
wpa_printf ( MSG_DEBUG ,
" EAP: No suitable EMSK available for ERP " ) ;
goto fail ;
}
wpa_hexdump_key ( MSG_DEBUG , " EAP: EMSK " , emsk , emsk_len ) ;
WPA_PUT_BE16 ( len , 8 ) ;
if ( hmac_sha256_kdf ( sm - > eap_if . eapSessionId , sm - > eap_if . eapSessionIdLen ,
" EMSK " , len , sizeof ( len ) ,
EMSKname , EAP_EMSK_NAME_LEN ) < 0 ) {
wpa_printf ( MSG_DEBUG , " EAP: Could not derive EMSKname " ) ;
goto fail ;
}
wpa_hexdump ( MSG_DEBUG , " EAP: EMSKname " , EMSKname , EAP_EMSK_NAME_LEN ) ;
pos = wpa_snprintf_hex ( erp - > keyname_nai , nai_buf_len ,
EMSKname , EAP_EMSK_NAME_LEN ) ;
erp - > keyname_nai [ pos ] = ' @ ' ;
os_memcpy ( & erp - > keyname_nai [ pos + 1 ] , domain , domain_len ) ;
WPA_PUT_BE16 ( len , emsk_len ) ;
if ( hmac_sha256_kdf ( emsk , emsk_len ,
" EAP Re-authentication Root Key@ietf.org " ,
len , sizeof ( len ) , erp - > rRK , emsk_len ) < 0 ) {
wpa_printf ( MSG_DEBUG , " EAP: Could not derive rRK for ERP " ) ;
goto fail ;
}
erp - > rRK_len = emsk_len ;
wpa_hexdump_key ( MSG_DEBUG , " EAP: ERP rRK " , erp - > rRK , erp - > rRK_len ) ;
if ( hmac_sha256_kdf ( erp - > rRK , erp - > rRK_len ,
" EAP Re-authentication Integrity Key@ietf.org " ,
len , sizeof ( len ) , erp - > rIK , erp - > rRK_len ) < 0 ) {
wpa_printf ( MSG_DEBUG , " EAP: Could not derive rIK for ERP " ) ;
goto fail ;
}
erp - > rIK_len = erp - > rRK_len ;
wpa_hexdump_key ( MSG_DEBUG , " EAP: ERP rIK " , erp - > rIK , erp - > rIK_len ) ;
if ( eap_erp_add_key ( sm , erp ) = = 0 ) {
wpa_printf ( MSG_DEBUG , " EAP: Stored ERP keys %s " ,
erp - > keyname_nai ) ;
erp = NULL ;
}
fail :
bin_clear_free ( emsk , emsk_len ) ;
bin_clear_free ( erp , sizeof ( * erp ) ) ;
# endif /* CONFIG_ERP */
}
SM_STATE ( EAP , METHOD_RESPONSE )
{
SM_ENTRY ( EAP , METHOD_RESPONSE ) ;
@ -416,6 +526,8 @@ SM_STATE(EAP, METHOD_RESPONSE)
sm - > eap_if . eapSessionId ,
sm - > eap_if . eapSessionIdLen ) ;
}
if ( sm - > erp & & sm - > m - > get_emsk & & sm - > eap_if . eapSessionId )
eap_server_erp_init ( sm ) ;
sm - > methodState = METHOD_END ;
} else {
sm - > methodState = METHOD_CONTINUE ;
@ -573,12 +685,307 @@ SM_STATE(EAP, INITIATE_REAUTH_START)
}
# ifdef CONFIG_ERP
static void erp_send_finish_reauth ( struct eap_sm * sm ,
struct eap_server_erp_key * erp , u8 id ,
u8 flags , u16 seq , const char * nai )
{
size_t plen ;
struct wpabuf * msg ;
u8 hash [ SHA256_MAC_LEN ] ;
size_t hash_len ;
u8 seed [ 4 ] ;
if ( erp ) {
switch ( erp - > cryptosuite ) {
case EAP_ERP_CS_HMAC_SHA256_256 :
hash_len = 32 ;
break ;
case EAP_ERP_CS_HMAC_SHA256_128 :
hash_len = 16 ;
break ;
default :
return ;
}
} else
hash_len = 0 ;
plen = 1 + 2 + 2 + os_strlen ( nai ) ;
if ( hash_len )
plen + = 1 + hash_len ;
msg = eap_msg_alloc ( EAP_VENDOR_IETF , EAP_ERP_TYPE_REAUTH , plen ,
EAP_CODE_FINISH , id ) ;
if ( msg = = NULL )
return ;
wpabuf_put_u8 ( msg , flags ) ;
wpabuf_put_be16 ( msg , seq ) ;
wpabuf_put_u8 ( msg , EAP_ERP_TLV_KEYNAME_NAI ) ;
wpabuf_put_u8 ( msg , os_strlen ( nai ) ) ;
wpabuf_put_str ( msg , nai ) ;
if ( erp ) {
wpabuf_put_u8 ( msg , erp - > cryptosuite ) ;
if ( hmac_sha256 ( erp - > rIK , erp - > rIK_len ,
wpabuf_head ( msg ) , wpabuf_len ( msg ) , hash ) < 0 ) {
wpabuf_free ( msg ) ;
return ;
}
wpabuf_put_data ( msg , hash , hash_len ) ;
}
wpa_printf ( MSG_DEBUG , " EAP: Send EAP-Finish/Re-auth (%s) " ,
flags & 0x80 ? " failure " : " success " ) ;
sm - > lastId = sm - > currentId ;
sm - > currentId = id ;
wpabuf_free ( sm - > eap_if . eapReqData ) ;
sm - > eap_if . eapReqData = msg ;
wpabuf_free ( sm - > lastReqData ) ;
sm - > lastReqData = NULL ;
if ( flags & 0x80 ) {
sm - > eap_if . eapFail = TRUE ;
wpa_msg ( sm - > msg_ctx , MSG_INFO , WPA_EVENT_EAP_FAILURE
MACSTR , MAC2STR ( sm - > peer_addr ) ) ;
return ;
}
bin_clear_free ( sm - > eap_if . eapKeyData , sm - > eap_if . eapKeyDataLen ) ;
sm - > eap_if . eapKeyDataLen = 0 ;
sm - > eap_if . eapKeyData = os_malloc ( erp - > rRK_len ) ;
if ( ! sm - > eap_if . eapKeyData )
return ;
WPA_PUT_BE16 ( seed , seq ) ;
WPA_PUT_BE16 ( & seed [ 2 ] , erp - > rRK_len ) ;
if ( hmac_sha256_kdf ( erp - > rRK , erp - > rRK_len ,
" Re-authentication Master Session Key@ietf.org " ,
seed , sizeof ( seed ) ,
sm - > eap_if . eapKeyData , erp - > rRK_len ) < 0 ) {
wpa_printf ( MSG_DEBUG , " EAP: Could not derive rMSK for ERP " ) ;
bin_clear_free ( sm - > eap_if . eapKeyData , erp - > rRK_len ) ;
sm - > eap_if . eapKeyData = NULL ;
return ;
}
sm - > eap_if . eapKeyDataLen = erp - > rRK_len ;
sm - > eap_if . eapKeyAvailable = TRUE ;
wpa_hexdump_key ( MSG_DEBUG , " EAP: ERP rMSK " ,
sm - > eap_if . eapKeyData , sm - > eap_if . eapKeyDataLen ) ;
sm - > eap_if . eapSuccess = TRUE ;
wpa_msg ( sm - > msg_ctx , MSG_INFO , WPA_EVENT_EAP_SUCCESS
MACSTR , MAC2STR ( sm - > peer_addr ) ) ;
}
SM_STATE ( EAP , INITIATE_RECEIVED )
{
const u8 * pos , * end , * start , * tlvs , * hdr ;
const struct eap_hdr * ehdr ;
size_t len ;
u8 flags ;
u16 seq ;
char nai [ 254 ] ;
struct eap_server_erp_key * erp ;
int max_len ;
u8 hash [ SHA256_MAC_LEN ] ;
size_t hash_len ;
struct erp_tlvs parse ;
u8 resp_flags = 0x80 ; /* default to failure; cleared on success */
SM_ENTRY ( EAP , INITIATE_RECEIVED ) ;
sm - > rxInitiate = FALSE ;
pos = eap_hdr_validate ( EAP_VENDOR_IETF , EAP_ERP_TYPE_REAUTH ,
sm - > eap_if . eapRespData , & len ) ;
if ( pos = = NULL ) {
wpa_printf ( MSG_INFO , " EAP-Initiate: Invalid frame " ) ;
goto fail ;
}
hdr = wpabuf_head ( sm - > eap_if . eapRespData ) ;
ehdr = wpabuf_head ( sm - > eap_if . eapRespData ) ;
wpa_hexdump ( MSG_DEBUG , " EAP: EAP-Initiate/Re-Auth " , pos , len ) ;
if ( len < 4 ) {
wpa_printf ( MSG_INFO , " EAP: Too short EAP-Initiate/Re-auth " ) ;
goto fail ;
}
end = pos + len ;
flags = * pos + + ;
seq = WPA_GET_BE16 ( pos ) ;
pos + = 2 ;
wpa_printf ( MSG_DEBUG , " EAP: Flags=0x%x SEQ=%u " , flags , seq ) ;
tlvs = pos ;
/*
* Parse TVs / TLVs . Since we do not yet know the length of the
* Authentication Tag , stop parsing if an unknown TV / TLV is seen and
* just try to find the keyName - NAI first so that we can check the
* Authentication Tag .
*/
if ( erp_parse_tlvs ( tlvs , end , & parse , 1 ) < 0 )
goto fail ;
if ( ! parse . keyname ) {
wpa_printf ( MSG_DEBUG ,
" EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet " ) ;
goto fail ;
}
wpa_hexdump_ascii ( MSG_DEBUG , " EAP: EAP-Initiate/Re-auth - keyName-NAI " ,
parse . keyname , parse . keyname_len ) ;
if ( parse . keyname_len > 253 ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Too long keyName-NAI in EAP-Initiate/Re-auth " ) ;
goto fail ;
}
os_memcpy ( nai , parse . keyname , parse . keyname_len ) ;
nai [ parse . keyname_len ] = ' \0 ' ;
if ( ! sm - > eap_server ) {
/*
* In passthrough case , EAP - Initiate / Re - auth replaces
* EAP Identity exchange . Use keyName - NAI as the user identity
* and forward EAP - Initiate / Re - auth to the backend
* authentication server .
*/
wpa_printf ( MSG_DEBUG ,
" EAP: Use keyName-NAI as user identity for backend authentication " ) ;
eap_server_clear_identity ( sm ) ;
sm - > identity = ( u8 * ) dup_binstr ( parse . keyname ,
parse . keyname_len ) ;
if ( ! sm - > identity )
goto fail ;
sm - > identity_len = parse . keyname_len ;
return ;
}
erp = eap_erp_get_key ( sm , nai ) ;
if ( ! erp ) {
wpa_printf ( MSG_DEBUG , " EAP: No matching ERP key found for %s " ,
nai ) ;
goto report_error ;
}
if ( erp - > recv_seq ! = ( u32 ) - 1 & & erp - > recv_seq > = seq ) {
wpa_printf ( MSG_DEBUG ,
" EAP: SEQ=%u replayed (already received SEQ=%u) " ,
seq , erp - > recv_seq ) ;
goto fail ;
}
/* Is there enough room for Cryptosuite and Authentication Tag? */
start = parse . keyname + parse . keyname_len ;
max_len = end - start ;
if ( max_len <
1 + ( erp - > cryptosuite = = EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16 ) ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Not enough room for Authentication Tag " ) ;
goto fail ;
}
switch ( erp - > cryptosuite ) {
case EAP_ERP_CS_HMAC_SHA256_256 :
if ( end [ - 33 ] ! = erp - > cryptosuite ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Different Cryptosuite used " ) ;
goto fail ;
}
hash_len = 32 ;
break ;
case EAP_ERP_CS_HMAC_SHA256_128 :
if ( end [ - 17 ] ! = erp - > cryptosuite ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Different Cryptosuite used " ) ;
goto fail ;
}
hash_len = 16 ;
break ;
default :
hash_len = 0 ;
break ;
}
if ( hash_len ) {
if ( hmac_sha256 ( erp - > rIK , erp - > rIK_len , hdr ,
end - hdr - hash_len , hash ) < 0 )
goto fail ;
if ( os_memcmp ( end - hash_len , hash , hash_len ) ! = 0 ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Authentication Tag mismatch " ) ;
goto fail ;
}
}
/* Check if any supported CS results in matching tag */
if ( ! hash_len & & max_len > = 1 + 32 & &
end [ - 33 ] = = EAP_ERP_CS_HMAC_SHA256_256 ) {
if ( hmac_sha256 ( erp - > rIK , erp - > rIK_len , hdr ,
end - hdr - 32 , hash ) < 0 )
goto fail ;
if ( os_memcmp ( end - 32 , hash , 32 ) = = 0 ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Authentication Tag match using HMAC-SHA256-256 " ) ;
hash_len = 32 ;
erp - > cryptosuite = EAP_ERP_CS_HMAC_SHA256_256 ;
}
}
if ( ! hash_len & & end [ - 17 ] = = EAP_ERP_CS_HMAC_SHA256_128 ) {
if ( hmac_sha256 ( erp - > rIK , erp - > rIK_len , hdr ,
end - hdr - 16 , hash ) < 0 )
goto fail ;
if ( os_memcmp ( end - 16 , hash , 16 ) = = 0 ) {
wpa_printf ( MSG_DEBUG ,
" EAP: Authentication Tag match using HMAC-SHA256-128 " ) ;
hash_len = 16 ;
erp - > cryptosuite = EAP_ERP_CS_HMAC_SHA256_128 ;
}
}
if ( ! hash_len ) {
wpa_printf ( MSG_DEBUG ,
" EAP: No supported cryptosuite matched Authentication Tag " ) ;
goto fail ;
}
end - = 1 + hash_len ;
/*
* Parse TVs / TLVs again now that we know the exact part of the buffer
* that contains them .
*/
wpa_hexdump ( MSG_DEBUG , " EAP: EAP-Initiate/Re-Auth TVs/TLVs " ,
tlvs , end - tlvs ) ;
if ( erp_parse_tlvs ( tlvs , end , & parse , 0 ) < 0 )
goto fail ;
wpa_printf ( MSG_DEBUG , " EAP: ERP key %s SEQ updated to %u " ,
erp - > keyname_nai , seq ) ;
erp - > recv_seq = seq ;
resp_flags & = ~ 0x80 ; /* R=0 - success */
report_error :
erp_send_finish_reauth ( sm , erp , ehdr - > identifier , resp_flags , seq , nai ) ;
return ;
fail :
sm - > ignore = TRUE ;
}
# endif /* CONFIG_ERP */
SM_STATE ( EAP , INITIALIZE_PASSTHROUGH )
{
SM_ENTRY ( EAP , INITIALIZE_PASSTHROUGH ) ;
wpabuf_free ( sm - > eap_if . aaaEapRespData ) ;
sm - > eap_if . aaaEapRespData = NULL ;
sm - > try_initiate_reauth = FALSE ;
}
@ -802,6 +1209,10 @@ SM_STEP(EAP)
sm - > respVendor = = EAP_VENDOR_IETF & &
sm - > respVendorMethod = = sm - > currentMethod ) ) )
SM_ENTER ( EAP , INTEGRITY_CHECK ) ;
# ifdef CONFIG_ERP
else if ( sm - > rxInitiate )
SM_ENTER ( EAP , INITIATE_RECEIVED ) ;
# endif /* CONFIG_ERP */
else {
wpa_printf ( MSG_DEBUG , " EAP: RECEIVED->DISCARD: "
" rxResp=%d respId=%d currentId=%d "
@ -892,12 +1303,20 @@ SM_STEP(EAP)
SM_ENTER ( EAP , INITIALIZE_PASSTHROUGH ) ;
else if ( sm - > decision = = DECISION_INITIATE_REAUTH_START )
SM_ENTER ( EAP , INITIATE_REAUTH_START ) ;
# ifdef CONFIG_ERP
else if ( sm - > eap_server & & sm - > erp & & sm - > rxInitiate )
SM_ENTER ( EAP , INITIATE_RECEIVED ) ;
# endif /* CONFIG_ERP */
else
SM_ENTER ( EAP , PROPOSE_METHOD ) ;
break ;
case EAP_INITIATE_REAUTH_START :
SM_ENTER ( EAP , SEND_REQUEST ) ;
break ;
case EAP_INITIATE_RECEIVED :
if ( ! sm - > eap_server )
SM_ENTER ( EAP , SELECT_ACTION ) ;
break ;
case EAP_TIMEOUT_FAILURE :
break ;
case EAP_FAILURE :
@ -1026,6 +1445,7 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
/* parse rxResp, respId, respMethod */
sm - > rxResp = FALSE ;
sm - > rxInitiate = FALSE ;
sm - > respId = - 1 ;
sm - > respMethod = EAP_TYPE_NONE ;
sm - > respVendor = EAP_VENDOR_IETF ;
@ -1052,6 +1472,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
if ( hdr - > code = = EAP_CODE_RESPONSE )
sm - > rxResp = TRUE ;
else if ( hdr - > code = = EAP_CODE_INITIATE )
sm - > rxInitiate = TRUE ;
if ( plen > sizeof ( * hdr ) ) {
u8 * pos = ( u8 * ) ( hdr + 1 ) ;
@ -1069,10 +1491,10 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
}
}
wpa_printf ( MSG_DEBUG , " EAP: parseEapResp: rxResp=%d respId=%d "
" respMethod=%u respVendor=%u respVendorMethod=%u" ,
sm - > rxResp , sm - > r espId, sm - > respMethod , sm - > respVendor ,
sm - > respVendor Method) ;
wpa_printf ( MSG_DEBUG ,
" EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u" ,
sm - > rxResp , sm - > r xInitiate, sm - > r espId, sm - > respMethod ,
sm - > respVendor , sm - > respVendor Method) ;
}
@ -1430,6 +1852,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
sm - > pbc_in_m1 = conf - > pbc_in_m1 ;
sm - > server_id = conf - > server_id ;
sm - > server_id_len = conf - > server_id_len ;
sm - > erp = conf - > erp ;
# ifdef CONFIG_TESTING_OPTIONS
sm - > tls_test_flags = conf - > tls_test_flags ;