2014-03-25 20:39:02 +01:00
/*
2018-12-27 15:19:46 +01:00
* IEEE 802.1 X - 2010 Key Agreement Protocol of PAE state machine
2014-03-25 20:39:02 +01:00
* Copyright ( c ) 2013 , Qualcomm Atheros , Inc .
*
* This software may be distributed under the terms of the BSD license .
* See README for more details .
*/
# include <time.h>
# include "includes.h"
# include "common.h"
# include "list.h"
# include "eloop.h"
# include "wpabuf.h"
# include "state_machine.h"
# include "l2_packet/l2_packet.h"
# include "common/eapol_common.h"
# include "crypto/aes_wrap.h"
# include "ieee802_1x_cp.h"
# include "ieee802_1x_key.h"
# include "ieee802_1x_kay.h"
# include "ieee802_1x_kay_i.h"
# include "ieee802_1x_secy_ops.h"
# define DEFAULT_SA_KEY_LEN 16
# define DEFAULT_ICV_LEN 16
# define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */
2018-02-20 20:28:44 +01:00
# define MAX_MISSING_SAK_USE 10 / * Accept up to 10 inbound MKPDUs without
* SAK - USE before dropping */
2014-03-25 20:39:02 +01:00
# define PENDING_PN_EXHAUSTION 0xC0000000
2016-08-12 15:07:33 +02:00
# define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3)
2014-03-25 20:39:02 +01:00
/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
# define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
static u8 mka_algo_agility [ 4 ] = MKA_ALGO_AGILITY_2009 ;
/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
static struct macsec_ciphersuite cipher_suite_tbl [ ] = {
/* GCM-AES-128 */
{
2016-08-12 15:07:33 +02:00
. id = CS_ID_GCM_AES_128 ,
. name = CS_NAME_GCM_AES_128 ,
. capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50 ,
. sak_len = DEFAULT_SA_KEY_LEN ,
2014-03-25 20:39:02 +01:00
} ,
2018-08-01 10:27:22 +02:00
/* GCM-AES-256 */
{
. id = CS_ID_GCM_AES_256 ,
. name = CS_NAME_GCM_AES_256 ,
. capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50 ,
. sak_len = 32 ,
} ,
2014-03-25 20:39:02 +01:00
} ;
# define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
# define DEFAULT_CS_INDEX 0
static struct mka_alg mka_alg_tbl [ ] = {
{
2016-08-12 15:07:33 +02:00
. parameter = MKA_ALGO_AGILITY_2009 ,
. icv_len = DEFAULT_ICV_LEN ,
2018-12-26 15:37:49 +01:00
. cak_trfm = ieee802_1x_cak_aes_cmac ,
. ckn_trfm = ieee802_1x_ckn_aes_cmac ,
2018-12-26 11:35:18 +01:00
. kek_trfm = ieee802_1x_kek_aes_cmac ,
2018-12-26 11:39:21 +01:00
. ick_trfm = ieee802_1x_ick_aes_cmac ,
2018-12-26 15:18:00 +01:00
. icv_hash = ieee802_1x_icv_aes_cmac ,
2014-03-25 20:39:02 +01:00
} ,
} ;
# define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
static int is_ki_equal ( struct ieee802_1x_mka_ki * ki1 ,
struct ieee802_1x_mka_ki * ki2 )
{
return os_memcmp ( ki1 - > mi , ki2 - > mi , MI_LEN ) = = 0 & &
ki1 - > kn = = ki2 - > kn ;
}
static void set_mka_param_body_len ( void * body , unsigned int len )
{
struct ieee802_1x_mka_hdr * hdr = body ;
hdr - > length = ( len > > 8 ) & 0x0f ;
hdr - > length1 = len & 0xff ;
}
static unsigned int get_mka_param_body_len ( const void * body )
{
const struct ieee802_1x_mka_hdr * hdr = body ;
return ( hdr - > length < < 8 ) | hdr - > length1 ;
}
2016-08-07 10:40:55 +02:00
static u8 get_mka_param_body_type ( const void * body )
2014-03-25 20:39:02 +01:00
{
const struct ieee802_1x_mka_hdr * hdr = body ;
return hdr - > type ;
}
2018-12-27 15:19:46 +01:00
static const char * mi_txt ( const u8 * mi )
{
static char txt [ MI_LEN * 2 + 1 ] ;
wpa_snprintf_hex ( txt , sizeof ( txt ) , mi , MI_LEN ) ;
return txt ;
}
static const char * sci_txt ( const struct ieee802_1x_mka_sci * sci )
{
static char txt [ ETH_ALEN * 3 + 1 + 5 + 1 ] ;
os_snprintf ( txt , sizeof ( txt ) , MACSTR " @%u " ,
MAC2STR ( sci - > addr ) , be_to_host16 ( sci - > port ) ) ;
return txt ;
}
static const char * algo_agility_txt ( const u8 * algo_agility )
{
static char txt [ 4 * 2 + 1 ] ;
wpa_snprintf_hex ( txt , sizeof ( txt ) , algo_agility , 4 ) ;
return txt ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_mka_dump_basic_body -
*/
static void
ieee802_1x_mka_dump_basic_body ( struct ieee802_1x_mka_basic_body * body )
{
size_t body_len ;
if ( ! body )
return ;
2018-12-27 15:19:46 +01:00
/* IEEE Std 802.1X-2010, Figure 11-8 */
2014-03-25 20:39:02 +01:00
body_len = get_mka_param_body_len ( body ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " MKA Basic Parameter Set " ) ;
wpa_printf ( MSG_DEBUG , " \t MKA Version Identifier: %d " , body - > version ) ;
wpa_printf ( MSG_DEBUG , " \t Key Server Priority: %d " , body - > priority ) ;
wpa_printf ( MSG_DEBUG , " \t Key Server: %d " , body - > key_server ) ;
wpa_printf ( MSG_DEBUG , " \t MACsec Desired: %d " , body - > macsec_desired ) ;
wpa_printf ( MSG_DEBUG , " \t MACsec Capability: %d " ,
body - > macsec_capability ) ;
wpa_printf ( MSG_DEBUG , " \t Parameter set body length: %zu " , body_len ) ;
wpa_printf ( MSG_DEBUG , " \t SCI: %s " , sci_txt ( & body - > actor_sci ) ) ;
wpa_printf ( MSG_DEBUG , " \t Actor's Member Identifier: %s " ,
mi_txt ( body - > actor_mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t Actor's Message Number: %d " ,
2014-03-25 20:39:02 +01:00
be_to_host32 ( body - > actor_mn ) ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Algorithm Agility: %s " ,
algo_agility_txt ( body - > algo_agility ) ) ;
wpa_hexdump ( MSG_DEBUG , " \t CAK Name " , body - > ckn ,
body_len + MKA_HDR_LEN - sizeof ( * body ) ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_dump_peer_body -
*/
static void
ieee802_1x_mka_dump_peer_body ( struct ieee802_1x_mka_peer_body * body )
{
size_t body_len ;
size_t i ;
u8 * mi ;
2016-06-24 00:38:48 +02:00
be32 mn ;
2014-03-25 20:39:02 +01:00
if ( body = = NULL )
return ;
2018-12-27 15:19:46 +01:00
/* IEEE Std 802.1X-2010, Figure 11-9 */
2014-03-25 20:39:02 +01:00
body_len = get_mka_param_body_len ( body ) ;
if ( body - > type = = MKA_LIVE_PEER_LIST ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " Live Peer List parameter set " ) ;
wpa_printf ( MSG_DEBUG , " \t Body Length: %zu " , body_len ) ;
2014-03-25 20:39:02 +01:00
} else if ( body - > type = = MKA_POTENTIAL_PEER_LIST ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " Potential Peer List parameter set " ) ;
wpa_printf ( MSG_DEBUG , " \t Body Length: %zu " , body_len ) ;
2014-03-25 20:39:02 +01:00
}
for ( i = 0 ; i < body_len ; i + = MI_LEN + sizeof ( mn ) ) {
mi = body - > peer + i ;
os_memcpy ( & mn , mi + MI_LEN , sizeof ( mn ) ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Member Id: %s Message Number: %d " ,
mi_txt ( mi ) , be_to_host32 ( mn ) ) ;
2014-03-25 20:39:02 +01:00
}
}
/**
* ieee802_1x_mka_dump_dist_sak_body -
*/
static void
ieee802_1x_mka_dump_dist_sak_body ( struct ieee802_1x_mka_dist_sak_body * body )
{
size_t body_len ;
if ( body = = NULL )
return ;
2018-12-27 15:19:46 +01:00
/* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */
2014-03-25 20:39:02 +01:00
body_len = get_mka_param_body_len ( body ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " Distributed SAK parameter set " ) ;
wpa_printf ( MSG_DEBUG , " \t Distributed AN........: %d " , body - > dan ) ;
wpa_printf ( MSG_DEBUG , " \t Confidentiality Offset: %d " ,
2014-03-25 20:39:02 +01:00
body - > confid_offset ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Body Length...........: %zu " , body_len ) ;
2014-03-25 20:39:02 +01:00
if ( ! body_len )
return ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Key Number............: %d " ,
2014-03-25 20:39:02 +01:00
be_to_host32 ( body - > kn ) ) ;
2018-12-27 15:19:46 +01:00
/* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
wpa_hexdump ( MSG_DEBUG , " \t AES Key Wrap of SAK...: " , body - > sak , 24 ) ;
2014-03-25 20:39:02 +01:00
}
static const char * yes_no ( int val )
{
return val ? " Yes " : " No " ;
}
/**
* ieee802_1x_mka_dump_sak_use_body -
*/
static void
ieee802_1x_mka_dump_sak_use_body ( struct ieee802_1x_mka_sak_use_body * body )
{
int body_len ;
if ( body = = NULL )
return ;
2018-12-27 15:19:46 +01:00
/* IEEE Std 802.1X-2010, Figure 11-10 */
2014-03-25 20:39:02 +01:00
body_len = get_mka_param_body_len ( body ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " MACsec SAK Use parameter set " ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " \t Latest Key AN....: %d " , body - > lan ) ;
wpa_printf ( MSG_DEBUG , " \t Latest Key Tx....: %s " , yes_no ( body - > ltx ) ) ;
wpa_printf ( MSG_DEBUG , " \t Latest Key Rx....: %s " , yes_no ( body - > lrx ) ) ;
2018-11-02 19:02:15 +01:00
wpa_printf ( MSG_DEBUG , " \t Old Key AN.......: %d " , body - > oan ) ;
wpa_printf ( MSG_DEBUG , " \t Old Key Tx.......: %s " , yes_no ( body - > otx ) ) ;
wpa_printf ( MSG_DEBUG , " \t Old Key Rx.......: %s " , yes_no ( body - > orx ) ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Plain Tx.........: %s " , yes_no ( body - > ptx ) ) ;
wpa_printf ( MSG_DEBUG , " \t Plain Rx.........: %s " , yes_no ( body - > prx ) ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " \t Delay Protect....: %s " ,
yes_no ( body - > delay_protect ) ) ;
wpa_printf ( MSG_DEBUG , " \t Body Length......: %d " , body_len ) ;
if ( ! body_len )
return ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Key Server MI....: %s " , mi_txt ( body - > lsrv_mi ) ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " \t Key Number.......: %u " ,
be_to_host32 ( body - > lkn ) ) ;
wpa_printf ( MSG_DEBUG , " \t Lowest PN........: %u " ,
be_to_host32 ( body - > llpn ) ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t Old Key Server MI: %s " , mi_txt ( body - > osrv_mi ) ) ;
2018-11-02 19:02:15 +01:00
wpa_printf ( MSG_DEBUG , " \t Old Key Number...: %u " ,
2014-03-25 20:39:02 +01:00
be_to_host32 ( body - > okn ) ) ;
2018-11-02 19:02:15 +01:00
wpa_printf ( MSG_DEBUG , " \t Old Lowest PN....: %u " ,
2014-03-25 20:39:02 +01:00
be_to_host32 ( body - > olpn ) ) ;
}
/**
* ieee802_1x_kay_get_participant -
*/
static struct ieee802_1x_mka_participant *
2018-02-20 20:28:31 +01:00
ieee802_1x_kay_get_participant ( struct ieee802_1x_kay * kay , const u8 * ckn ,
size_t len )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * participant ;
dl_list_for_each ( participant , & kay - > participant_list ,
struct ieee802_1x_mka_participant , list ) {
2018-02-20 20:28:31 +01:00
if ( participant - > ckn . len = = len & &
os_memcmp ( participant - > ckn . name , ckn ,
2014-03-25 20:39:02 +01:00
participant - > ckn . len ) = = 0 )
return participant ;
}
wpa_printf ( MSG_DEBUG , " KaY: participant is not found " ) ;
return NULL ;
}
/**
* ieee802_1x_kay_get_principal_participant -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_principal_participant ( struct ieee802_1x_kay * kay )
{
struct ieee802_1x_mka_participant * participant ;
dl_list_for_each ( participant , & kay - > participant_list ,
struct ieee802_1x_mka_participant , list ) {
if ( participant - > principal )
return participant ;
}
2016-08-15 11:43:42 +02:00
wpa_printf ( MSG_DEBUG , " KaY: principal participant is not found " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
static struct ieee802_1x_kay_peer * get_peer_mi ( struct dl_list * peers ,
const u8 * mi )
{
struct ieee802_1x_kay_peer * peer ;
dl_list_for_each ( peer , peers , struct ieee802_1x_kay_peer , list ) {
if ( os_memcmp ( peer - > mi , mi , MI_LEN ) = = 0 )
return peer ;
}
return NULL ;
}
2016-08-15 11:43:41 +02:00
/**
* ieee802_1x_kay_get_potential_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_potential_peer (
struct ieee802_1x_mka_participant * participant , const u8 * mi )
{
return get_peer_mi ( & participant - > potential_peers , mi ) ;
}
/**
* ieee802_1x_kay_get_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_live_peer ( struct ieee802_1x_mka_participant * participant ,
const u8 * mi )
{
return get_peer_mi ( & participant - > live_peers , mi ) ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_is_in_potential_peer
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_kay_is_in_potential_peer (
struct ieee802_1x_mka_participant * participant , const u8 * mi )
{
2016-08-15 11:43:41 +02:00
return ieee802_1x_kay_get_potential_peer ( participant , mi ) ! = NULL ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_is_in_live_peer
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_kay_is_in_live_peer (
struct ieee802_1x_mka_participant * participant , const u8 * mi )
{
2016-08-15 11:43:41 +02:00
return ieee802_1x_kay_get_live_peer ( participant , mi ) ! = NULL ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_get_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer ( struct ieee802_1x_mka_participant * participant ,
const u8 * mi )
{
struct ieee802_1x_kay_peer * peer ;
2016-08-15 11:43:41 +02:00
peer = ieee802_1x_kay_get_live_peer ( participant , mi ) ;
2014-03-25 20:39:02 +01:00
if ( peer )
return peer ;
2016-08-15 11:43:41 +02:00
return ieee802_1x_kay_get_potential_peer ( participant , mi ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_get_cipher_suite
*/
static struct macsec_ciphersuite *
ieee802_1x_kay_get_cipher_suite ( struct ieee802_1x_mka_participant * participant ,
2018-12-27 15:37:57 +01:00
const u8 * cs_id , unsigned int * idx )
2014-03-25 20:39:02 +01:00
{
unsigned int i ;
2016-08-12 15:07:35 +02:00
u64 cs ;
be64 _cs ;
os_memcpy ( & _cs , cs_id , CS_ID_LEN ) ;
cs = be_to_host64 ( _cs ) ;
2014-03-25 20:39:02 +01:00
for ( i = 0 ; i < CS_TABLE_SIZE ; i + + ) {
2018-12-27 15:37:57 +01:00
if ( cipher_suite_tbl [ i ] . id = = cs ) {
* idx = i ;
2016-08-15 11:43:41 +02:00
return & cipher_suite_tbl [ i ] ;
2018-12-27 15:37:57 +01:00
}
2014-03-25 20:39:02 +01:00
}
2016-08-15 11:43:41 +02:00
return NULL ;
2014-03-25 20:39:02 +01:00
}
2016-11-27 20:08:55 +01:00
u64 mka_sci_u64 ( struct ieee802_1x_mka_sci * sci )
{
struct ieee802_1x_mka_sci tmp ;
os_memcpy ( tmp . addr , sci - > addr , ETH_ALEN ) ;
tmp . port = sci - > port ;
return * ( ( u64 * ) & tmp ) ;
}
2020-04-24 00:27:57 +02:00
static bool sci_equal ( const struct ieee802_1x_mka_sci * a ,
const struct ieee802_1x_mka_sci * b )
2016-08-15 11:43:41 +02:00
{
return os_memcmp ( a , b , sizeof ( struct ieee802_1x_mka_sci ) ) = = 0 ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_get_peer_sci
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer_sci ( struct ieee802_1x_mka_participant * participant ,
const struct ieee802_1x_mka_sci * sci )
{
struct ieee802_1x_kay_peer * peer ;
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
2016-08-15 11:43:41 +02:00
if ( sci_equal ( & peer - > sci , sci ) )
2014-03-25 20:39:02 +01:00
return peer ;
}
dl_list_for_each ( peer , & participant - > potential_peers ,
struct ieee802_1x_kay_peer , list ) {
2016-08-15 11:43:41 +02:00
if ( sci_equal ( & peer - > sci , sci ) )
2014-03-25 20:39:02 +01:00
return peer ;
}
return NULL ;
}
2016-10-21 14:45:29 +02:00
static void ieee802_1x_kay_use_data_key ( struct data_key * pkey ) ;
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_init_receive_sa -
*/
static struct receive_sa *
ieee802_1x_kay_init_receive_sa ( struct receive_sc * psc , u8 an , u32 lowest_pn ,
struct data_key * key )
{
struct receive_sa * psa ;
if ( ! psc | | ! key )
return NULL ;
psa = os_zalloc ( sizeof ( * psa ) ) ;
if ( ! psa ) {
wpa_printf ( MSG_ERROR , " %s: out of memory " , __func__ ) ;
return NULL ;
}
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_use_data_key ( key ) ;
2014-03-25 20:39:02 +01:00
psa - > pkey = key ;
psa - > lowest_pn = lowest_pn ;
psa - > next_pn = lowest_pn ;
psa - > an = an ;
psa - > sc = psc ;
os_get_time ( & psa - > created_time ) ;
2020-04-24 00:27:57 +02:00
psa - > in_use = false ;
2014-03-25 20:39:02 +01:00
dl_list_add ( & psc - > sa_list , & psa - > list ) ;
wpa_printf ( MSG_DEBUG ,
2018-06-15 17:06:41 +02:00
" KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC " ,
2016-10-21 14:45:26 +02:00
an , lowest_pn ) ;
2014-03-25 20:39:02 +01:00
return psa ;
}
2016-10-21 14:45:29 +02:00
static void ieee802_1x_kay_deinit_data_key ( struct data_key * pkey ) ;
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_deinit_receive_sa -
*/
static void ieee802_1x_kay_deinit_receive_sa ( struct receive_sa * psa )
{
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_deinit_data_key ( psa - > pkey ) ;
2014-03-25 20:39:02 +01:00
psa - > pkey = NULL ;
wpa_printf ( MSG_DEBUG ,
2016-08-12 15:07:33 +02:00
" KaY: Delete receive SA(an: %hhu) of SC " ,
psa - > an ) ;
2014-03-25 20:39:02 +01:00
dl_list_del ( & psa - > list ) ;
os_free ( psa ) ;
}
/**
* ieee802_1x_kay_init_receive_sc -
*/
static struct receive_sc *
2016-10-21 14:45:26 +02:00
ieee802_1x_kay_init_receive_sc ( const struct ieee802_1x_mka_sci * psci )
2014-03-25 20:39:02 +01:00
{
struct receive_sc * psc ;
if ( ! psci )
return NULL ;
psc = os_zalloc ( sizeof ( * psc ) ) ;
if ( ! psc ) {
wpa_printf ( MSG_ERROR , " %s: out of memory " , __func__ ) ;
return NULL ;
}
os_memcpy ( & psc - > sci , psci , sizeof ( psc - > sci ) ) ;
os_get_time ( & psc - > created_time ) ;
2020-04-24 00:27:57 +02:00
psc - > receiving = false ;
2014-03-25 20:39:02 +01:00
dl_list_init ( & psc - > sa_list ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Create receive SC: SCI %s " ,
sci_txt ( & psc - > sci ) ) ;
2014-03-25 20:39:02 +01:00
return psc ;
}
2016-10-21 14:45:28 +02:00
static void ieee802_1x_delete_receive_sa ( struct ieee802_1x_kay * kay ,
struct receive_sa * sa )
{
secy_disable_receive_sa ( kay , sa ) ;
secy_delete_receive_sa ( kay , sa ) ;
ieee802_1x_kay_deinit_receive_sa ( sa ) ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_deinit_receive_sc -
* */
static void
ieee802_1x_kay_deinit_receive_sc (
struct ieee802_1x_mka_participant * participant , struct receive_sc * psc )
{
struct receive_sa * psa , * pre_sa ;
2016-10-21 14:45:26 +02:00
wpa_printf ( MSG_DEBUG , " KaY: Delete receive SC " ) ;
2014-03-25 20:39:02 +01:00
dl_list_for_each_safe ( psa , pre_sa , & psc - > sa_list , struct receive_sa ,
2016-10-21 14:45:28 +02:00
list )
ieee802_1x_delete_receive_sa ( participant - > kay , psa ) ;
2014-03-25 20:39:02 +01:00
dl_list_del ( & psc - > list ) ;
2017-03-16 14:01:54 +01:00
secy_delete_receive_sc ( participant - > kay , psc ) ;
2014-03-25 20:39:02 +01:00
os_free ( psc ) ;
}
2016-08-15 11:43:41 +02:00
static void ieee802_1x_kay_dump_peer ( struct ieee802_1x_kay_peer * peer )
{
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " \t MI: %s MN: %d SCI: %s " ,
mi_txt ( peer - > mi ) , peer - > mn , sci_txt ( & peer - > sci ) ) ;
2016-08-15 11:43:41 +02:00
}
2014-03-25 20:39:02 +01:00
static struct ieee802_1x_kay_peer *
2016-08-15 11:43:41 +02:00
ieee802_1x_kay_create_peer ( const u8 * mi , u32 mn )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_kay_peer * peer ;
peer = os_zalloc ( sizeof ( * peer ) ) ;
2016-08-15 11:43:41 +02:00
if ( ! peer ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR , " KaY-%s: out of memory " , __func__ ) ;
return NULL ;
}
os_memcpy ( peer - > mi , mi , MI_LEN ) ;
peer - > mn = mn ;
peer - > expire = time ( NULL ) + MKA_LIFE_TIME / 1000 ;
2020-04-24 00:27:57 +02:00
peer - > sak_used = false ;
2018-02-20 20:28:44 +01:00
peer - > missing_sak_use_count = 0 ;
2016-08-15 11:43:41 +02:00
return peer ;
}
/**
* ieee802_1x_kay_create_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_live_peer ( struct ieee802_1x_mka_participant * participant ,
const u8 * mi , u32 mn )
{
struct ieee802_1x_kay_peer * peer ;
struct receive_sc * rxsc ;
peer = ieee802_1x_kay_create_peer ( mi , mn ) ;
if ( ! peer )
return NULL ;
2014-03-25 20:39:02 +01:00
os_memcpy ( & peer - > sci , & participant - > current_peer_sci ,
sizeof ( peer - > sci ) ) ;
2016-10-21 14:45:26 +02:00
rxsc = ieee802_1x_kay_init_receive_sc ( & peer - > sci ) ;
2016-07-19 11:56:52 +02:00
if ( ! rxsc ) {
os_free ( peer ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
2016-07-19 11:56:52 +02:00
}
2014-03-25 20:39:02 +01:00
2018-11-02 19:02:16 +01:00
if ( secy_create_receive_sc ( participant - > kay , rxsc ) ) {
os_free ( rxsc ) ;
os_free ( peer ) ;
return NULL ;
}
2016-07-19 11:56:52 +02:00
dl_list_add ( & participant - > live_peers , & peer - > list ) ;
2014-03-25 20:39:02 +01:00
dl_list_add ( & participant - > rxsc_list , & rxsc - > list ) ;
wpa_printf ( MSG_DEBUG , " KaY: Live peer created " ) ;
2016-08-15 11:43:41 +02:00
ieee802_1x_kay_dump_peer ( peer ) ;
2014-03-25 20:39:02 +01:00
return peer ;
}
/**
* ieee802_1x_kay_create_potential_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_potential_peer (
struct ieee802_1x_mka_participant * participant , const u8 * mi , u32 mn )
{
struct ieee802_1x_kay_peer * peer ;
2016-08-15 11:43:41 +02:00
peer = ieee802_1x_kay_create_peer ( mi , mn ) ;
if ( ! peer )
2014-03-25 20:39:02 +01:00
return NULL ;
dl_list_add ( & participant - > potential_peers , & peer - > list ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Potential peer created " ) ;
2016-08-15 11:43:41 +02:00
ieee802_1x_kay_dump_peer ( peer ) ;
2014-03-25 20:39:02 +01:00
return peer ;
}
/**
* ieee802_1x_kay_move_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_move_live_peer ( struct ieee802_1x_mka_participant * participant ,
u8 * mi , u32 mn )
{
struct ieee802_1x_kay_peer * peer ;
struct receive_sc * rxsc ;
2016-08-15 11:43:41 +02:00
peer = ieee802_1x_kay_get_potential_peer ( participant , mi ) ;
2017-04-24 10:38:10 +02:00
if ( ! peer )
return NULL ;
2014-03-25 20:39:02 +01:00
2016-10-21 14:45:26 +02:00
rxsc = ieee802_1x_kay_init_receive_sc ( & participant - > current_peer_sci ) ;
2016-07-19 11:56:54 +02:00
if ( ! rxsc )
return NULL ;
2014-03-25 20:39:02 +01:00
os_memcpy ( & peer - > sci , & participant - > current_peer_sci ,
sizeof ( peer - > sci ) ) ;
peer - > mn = mn ;
peer - > expire = time ( NULL ) + MKA_LIFE_TIME / 1000 ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Move potential peer to live peer " ) ;
2016-08-15 11:43:41 +02:00
ieee802_1x_kay_dump_peer ( peer ) ;
2014-03-25 20:39:02 +01:00
dl_list_del ( & peer - > list ) ;
2018-11-02 19:02:16 +01:00
if ( secy_create_receive_sc ( participant - > kay , rxsc ) ) {
wpa_printf ( MSG_ERROR , " KaY: Can't create SC, discard peer " ) ;
os_free ( rxsc ) ;
os_free ( peer ) ;
return NULL ;
}
2014-03-25 20:39:02 +01:00
dl_list_add_tail ( & participant - > live_peers , & peer - > list ) ;
dl_list_add ( & participant - > rxsc_list , & rxsc - > list ) ;
return peer ;
}
/**
* ieee802_1x_mka_basic_body_present -
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_basic_body_present (
struct ieee802_1x_mka_participant * participant )
{
2020-04-24 00:27:57 +02:00
return true ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_basic_body_length -
*/
static int
ieee802_1x_mka_basic_body_length ( struct ieee802_1x_mka_participant * participant )
{
int length ;
length = sizeof ( struct ieee802_1x_mka_basic_body ) ;
length + = participant - > ckn . len ;
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( length ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_encode_basic_body
*/
static int
ieee802_1x_mka_encode_basic_body (
struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_basic_body * body ;
struct ieee802_1x_kay * kay = participant - > kay ;
2017-08-17 23:51:58 +02:00
unsigned int length = sizeof ( struct ieee802_1x_mka_basic_body ) ;
2014-03-25 20:39:02 +01:00
2017-08-17 23:51:58 +02:00
length + = participant - > ckn . len ;
body = wpabuf_put ( buf , MKA_ALIGN_LENGTH ( length ) ) ;
2014-03-25 20:39:02 +01:00
body - > version = kay - > mka_version ;
body - > priority = kay - > actor_priority ;
2018-12-27 15:19:46 +01:00
/* The Key Server flag is set if and only if the participant has not
* decided that another participant is or will be the Key Server . */
2014-03-25 20:39:02 +01:00
if ( participant - > is_elected )
body - > key_server = participant - > is_key_server ;
else
body - > key_server = participant - > can_be_key_server ;
body - > macsec_desired = kay - > macsec_desired ;
2016-08-15 11:43:41 +02:00
body - > macsec_capability = kay - > macsec_capable ;
2014-03-25 20:39:02 +01:00
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
os_memcpy ( body - > actor_sci . addr , kay - > actor_sci . addr ,
sizeof ( kay - > actor_sci . addr ) ) ;
2016-06-24 16:48:27 +02:00
body - > actor_sci . port = kay - > actor_sci . port ;
2014-03-25 20:39:02 +01:00
os_memcpy ( body - > actor_mi , participant - > mi , sizeof ( body - > actor_mi ) ) ;
participant - > mn = participant - > mn + 1 ;
body - > actor_mn = host_to_be32 ( participant - > mn ) ;
2016-08-15 11:43:42 +02:00
os_memcpy ( body - > algo_agility , kay - > algo_agility ,
2014-03-25 20:39:02 +01:00
sizeof ( body - > algo_agility ) ) ;
os_memcpy ( body - > ckn , participant - > ckn . name , participant - > ckn . len ) ;
ieee802_1x_mka_dump_basic_body ( body ) ;
return 0 ;
}
2020-04-24 00:27:57 +02:00
static bool
2016-08-12 15:07:33 +02:00
reset_participant_mi ( struct ieee802_1x_mka_participant * participant )
{
if ( os_get_random ( participant - > mi , sizeof ( participant - > mi ) ) < 0 )
2020-04-24 00:27:57 +02:00
return false ;
2016-08-12 15:07:33 +02:00
participant - > mn = 0 ;
2020-04-24 00:27:57 +02:00
return true ;
2016-08-12 15:07:33 +02:00
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_mka_decode_basic_body -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_mka_decode_basic_body ( struct ieee802_1x_kay * kay , const u8 * mka_msg ,
size_t msg_len )
{
struct ieee802_1x_mka_participant * participant ;
const struct ieee802_1x_mka_basic_body * body ;
struct ieee802_1x_kay_peer * peer ;
2018-02-20 20:28:31 +01:00
size_t ckn_len ;
size_t body_len ;
2014-03-25 20:39:02 +01:00
body = ( const struct ieee802_1x_mka_basic_body * ) mka_msg ;
if ( body - > version > MKA_VERSION_ID ) {
wpa_printf ( MSG_DEBUG ,
2018-12-27 15:19:46 +01:00
" KaY: Peer's version(%d) greater than MKA current version(%d) " ,
2014-03-25 20:39:02 +01:00
body - > version , MKA_VERSION_ID ) ;
}
if ( kay - > is_obliged_key_server & & body - > key_server ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: I must be key server - ignore MKPDU claiming to be from a key server " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
2018-02-20 20:28:31 +01:00
body_len = get_mka_param_body_len ( body ) ;
if ( body_len < sizeof ( struct ieee802_1x_mka_basic_body ) - MKA_HDR_LEN ) {
wpa_printf ( MSG_DEBUG , " KaY: Too small body length %zu " ,
body_len ) ;
return NULL ;
}
ckn_len = body_len -
( sizeof ( struct ieee802_1x_mka_basic_body ) - MKA_HDR_LEN ) ;
participant = ieee802_1x_kay_get_participant ( kay , body - > ckn , ckn_len ) ;
2014-03-25 20:39:02 +01:00
if ( ! participant ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: Peer is not included in my CA - ignore MKPDU " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
/* If the peer's MI is my MI, I will choose new MI */
if ( os_memcmp ( body - > actor_mi , participant - > mi , MI_LEN ) = = 0 ) {
2016-08-12 15:07:33 +02:00
if ( ! reset_participant_mi ( participant ) )
2014-10-11 17:46:35 +02:00
return NULL ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: Peer using my MI - selected a new random MI: %s " ,
mi_txt ( participant - > mi ) ) ;
2014-03-25 20:39:02 +01:00
}
os_memcpy ( participant - > current_peer_id . mi , body - > actor_mi , MI_LEN ) ;
2016-06-24 16:53:29 +02:00
participant - > current_peer_id . mn = body - > actor_mn ;
2014-03-25 20:39:02 +01:00
os_memcpy ( participant - > current_peer_sci . addr , body - > actor_sci . addr ,
sizeof ( participant - > current_peer_sci . addr ) ) ;
2016-06-24 16:48:27 +02:00
participant - > current_peer_sci . port = body - > actor_sci . port ;
2014-03-25 20:39:02 +01:00
/* handler peer */
peer = ieee802_1x_kay_get_peer ( participant , body - > actor_mi ) ;
if ( ! peer ) {
2018-02-20 20:28:42 +01:00
/* Check duplicated SCI
*
* A duplicated SCI indicates either an active attacker or
* a valid peer whose MI is being changed . The latter scenario
* is more likely because to have gotten this far the received
* MKPDU must have had a valid ICV , indicating the peer holds
* the same CAK as our participant .
*
* Before creating a new peer object for the new MI we must
* clean up the resources ( SCs and SAs ) associated with the
* old peer . An easy way to do this is to ignore MKPDUs with
* the new MI ' s for now and just wait for the old peer to
* time out and clean itself up ( within MKA_LIFE_TIME ) .
*
* This method is preferable to deleting the old peer here
* and now and continuing on with processing because if this
* MKPDU is from an attacker it ' s better to ignore the MKPDU
* than to process it ( and delete a valid peer as well ) .
2014-03-25 20:39:02 +01:00
*/
peer = ieee802_1x_kay_get_peer_sci ( participant ,
& body - > actor_sci ) ;
if ( peer ) {
2018-11-02 19:02:19 +01:00
time_t new_expire ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING ,
2018-02-20 20:28:42 +01:00
" KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU " ) ;
2018-11-02 19:02:19 +01:00
/* Reduce timeout to speed up this process but left the
* chance for old one to prove aliveness . */
new_expire = time ( NULL ) + MKA_HELLO_TIME * 1.5 / 1000 ;
if ( peer - > expire > new_expire )
peer - > expire = new_expire ;
2018-02-20 20:28:42 +01:00
return NULL ;
2014-03-25 20:39:02 +01:00
}
peer = ieee802_1x_kay_create_potential_peer (
participant , body - > actor_mi ,
be_to_host32 ( body - > actor_mn ) ) ;
2018-12-27 15:19:46 +01:00
if ( ! peer ) {
wpa_printf ( MSG_DEBUG ,
" KaY: No potential peer entry found - ignore MKPDU " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
2018-12-27 15:19:46 +01:00
}
2014-03-25 20:39:02 +01:00
peer - > macsec_desired = body - > macsec_desired ;
2016-08-15 11:43:41 +02:00
peer - > macsec_capability = body - > macsec_capability ;
2020-04-24 00:27:57 +02:00
peer - > is_key_server = body - > key_server ;
2014-03-25 20:39:02 +01:00
peer - > key_server_priority = body - > priority ;
} else if ( peer - > mn < be_to_host32 ( body - > actor_mn ) ) {
peer - > mn = be_to_host32 ( body - > actor_mn ) ;
peer - > macsec_desired = body - > macsec_desired ;
2016-08-15 11:43:41 +02:00
peer - > macsec_capability = body - > macsec_capability ;
2020-04-24 00:27:57 +02:00
peer - > is_key_server = body - > key_server ;
2014-03-25 20:39:02 +01:00
peer - > key_server_priority = body - > priority ;
} else {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_WARNING ,
" KaY: The peer MN did not increase - ignore MKPDU " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
return participant ;
}
/**
* ieee802_1x_mka_live_peer_body_present
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_live_peer_body_present (
struct ieee802_1x_mka_participant * participant )
{
return ! dl_list_empty ( & participant - > live_peers ) ;
}
/**
* ieee802_1x_kay_get_live_peer_length
*/
static int
ieee802_1x_mka_get_live_peer_length (
struct ieee802_1x_mka_participant * participant )
{
int len = MKA_HDR_LEN ;
struct ieee802_1x_kay_peer * peer ;
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list )
len + = sizeof ( struct ieee802_1x_mka_peer_id ) ;
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( len ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_encode_live_peer_body -
*/
static int
ieee802_1x_mka_encode_live_peer_body (
struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_peer_body * body ;
struct ieee802_1x_kay_peer * peer ;
unsigned int length ;
struct ieee802_1x_mka_peer_id * body_peer ;
length = ieee802_1x_mka_get_live_peer_length ( participant ) ;
body = wpabuf_put ( buf , sizeof ( struct ieee802_1x_mka_peer_body ) ) ;
body - > type = MKA_LIVE_PEER_LIST ;
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
body_peer = wpabuf_put ( buf ,
sizeof ( struct ieee802_1x_mka_peer_id ) ) ;
os_memcpy ( body_peer - > mi , peer - > mi , MI_LEN ) ;
body_peer - > mn = host_to_be32 ( peer - > mn ) ;
}
ieee802_1x_mka_dump_peer_body ( body ) ;
return 0 ;
}
/**
* ieee802_1x_mka_potential_peer_body_present
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_potential_peer_body_present (
struct ieee802_1x_mka_participant * participant )
{
return ! dl_list_empty ( & participant - > potential_peers ) ;
}
/**
* ieee802_1x_kay_get_potential_peer_length
*/
static int
ieee802_1x_mka_get_potential_peer_length (
struct ieee802_1x_mka_participant * participant )
{
int len = MKA_HDR_LEN ;
struct ieee802_1x_kay_peer * peer ;
dl_list_for_each ( peer , & participant - > potential_peers ,
struct ieee802_1x_kay_peer , list )
len + = sizeof ( struct ieee802_1x_mka_peer_id ) ;
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( len ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_encode_potential_peer_body -
*/
static int
ieee802_1x_mka_encode_potential_peer_body (
struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_peer_body * body ;
struct ieee802_1x_kay_peer * peer ;
unsigned int length ;
struct ieee802_1x_mka_peer_id * body_peer ;
length = ieee802_1x_mka_get_potential_peer_length ( participant ) ;
body = wpabuf_put ( buf , sizeof ( struct ieee802_1x_mka_peer_body ) ) ;
body - > type = MKA_POTENTIAL_PEER_LIST ;
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
dl_list_for_each ( peer , & participant - > potential_peers ,
struct ieee802_1x_kay_peer , list ) {
body_peer = wpabuf_put ( buf ,
sizeof ( struct ieee802_1x_mka_peer_id ) ) ;
os_memcpy ( body_peer - > mi , peer - > mi , MI_LEN ) ;
body_peer - > mn = host_to_be32 ( peer - > mn ) ;
}
ieee802_1x_mka_dump_peer_body ( body ) ;
return 0 ;
}
/**
* ieee802_1x_mka_i_in_peerlist -
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_i_in_peerlist ( struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
struct ieee802_1x_mka_hdr * hdr ;
size_t body_len ;
size_t left_len ;
2016-08-07 10:40:55 +02:00
u8 body_type ;
2014-03-25 20:39:02 +01:00
const u8 * pos ;
size_t i ;
2016-08-12 15:07:33 +02:00
for ( pos = mka_msg , left_len = msg_len ;
left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN ;
2017-08-17 23:51:58 +02:00
left_len - = MKA_ALIGN_LENGTH ( body_len ) + MKA_HDR_LEN ,
pos + = MKA_ALIGN_LENGTH ( body_len ) + MKA_HDR_LEN ) {
2014-03-25 20:39:02 +01:00
hdr = ( struct ieee802_1x_mka_hdr * ) pos ;
body_len = get_mka_param_body_len ( hdr ) ;
body_type = get_mka_param_body_type ( hdr ) ;
2017-08-18 01:14:28 +02:00
if ( left_len < ( MKA_HDR_LEN + MKA_ALIGN_LENGTH ( body_len ) + DEFAULT_ICV_LEN ) ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV " ,
left_len , MKA_HDR_LEN ,
2017-08-18 01:14:28 +02:00
MKA_ALIGN_LENGTH ( body_len ) ,
DEFAULT_ICV_LEN ) ;
2020-04-24 00:27:57 +02:00
return false ;
2014-03-25 20:39:02 +01:00
}
2017-08-18 01:14:28 +02:00
if ( body_type ! = MKA_LIVE_PEER_LIST & &
body_type ! = MKA_POTENTIAL_PEER_LIST )
continue ;
2014-03-25 20:39:02 +01:00
if ( ( body_len % 16 ) ! = 0 ) {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets " ,
body_len ) ;
2016-08-12 15:07:33 +02:00
continue ;
2014-03-25 20:39:02 +01:00
}
2017-08-18 01:14:28 +02:00
ieee802_1x_mka_dump_peer_body (
( struct ieee802_1x_mka_peer_body * ) pos ) ;
2016-08-12 15:07:33 +02:00
for ( i = 0 ; i < body_len ;
i + = sizeof ( struct ieee802_1x_mka_peer_id ) ) {
const struct ieee802_1x_mka_peer_id * peer_mi ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:33 +02:00
peer_mi = ( const struct ieee802_1x_mka_peer_id * )
( pos + MKA_HDR_LEN + i ) ;
if ( os_memcmp ( peer_mi - > mi , participant - > mi ,
2018-12-27 15:19:46 +01:00
MI_LEN ) = = 0 ) {
u32 mn = be_to_host32 ( peer_mi - > mn ) ;
wpa_printf ( MSG_DEBUG ,
" KaY: My MI - received MN %u, most recently transmitted MN %u " ,
mn , participant - > mn ) ;
2019-07-29 16:05:40 +02:00
/* IEEE Std 802.1X-2010 is not exactly clear
* which values of MN should be accepted here .
* It uses " acceptably recent MN " language
* without defining what would be acceptable
* recent . For now , allow the last two used MN
* values ( i . e . , peer having copied my MI , MN
* from either of the last two MKPDUs that I
* have sent ) . */
if ( mn = = participant - > mn | |
( participant - > mn > 1 & &
mn = = participant - > mn - 1 ) )
2020-04-24 00:27:57 +02:00
return true ;
2018-12-27 15:19:46 +01:00
}
2016-08-12 15:07:33 +02:00
}
2014-03-25 20:39:02 +01:00
}
2020-04-24 00:27:57 +02:00
return false ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_decode_live_peer_body -
*/
static int ieee802_1x_mka_decode_live_peer_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * peer_msg , size_t msg_len )
{
const struct ieee802_1x_mka_hdr * hdr ;
struct ieee802_1x_kay_peer * peer ;
size_t body_len ;
size_t i ;
2020-04-24 00:27:57 +02:00
bool is_included ;
2014-03-25 20:39:02 +01:00
is_included = ieee802_1x_kay_is_in_live_peer (
participant , participant - > current_peer_id . mi ) ;
hdr = ( const struct ieee802_1x_mka_hdr * ) peer_msg ;
body_len = get_mka_param_body_len ( hdr ) ;
2016-07-19 11:56:57 +02:00
if ( body_len % 16 ! = 0 ) {
wpa_printf ( MSG_ERROR ,
" KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets " ,
body_len ) ;
return - 1 ;
}
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:33 +02:00
for ( i = 0 ; i < body_len ; i + = sizeof ( struct ieee802_1x_mka_peer_id ) ) {
const struct ieee802_1x_mka_peer_id * peer_mi ;
u32 peer_mn ;
peer_mi = ( const struct ieee802_1x_mka_peer_id * )
( peer_msg + MKA_HDR_LEN + i ) ;
peer_mn = be_to_host32 ( peer_mi - > mn ) ;
2014-03-25 20:39:02 +01:00
/* it is myself */
if ( os_memcmp ( peer_mi , participant - > mi , MI_LEN ) = = 0 ) {
/* My message id is used by other participant */
2016-08-12 15:07:33 +02:00
if ( peer_mn > participant - > mn & &
! reset_participant_mi ( participant ) )
wpa_printf ( MSG_DEBUG , " KaY: Could not update mi " ) ;
2014-03-25 20:39:02 +01:00
continue ;
}
2016-08-12 15:07:33 +02:00
2014-03-25 20:39:02 +01:00
if ( ! is_included )
continue ;
2016-08-12 15:07:33 +02:00
peer = ieee802_1x_kay_get_peer ( participant , peer_mi - > mi ) ;
if ( peer ) {
2014-03-25 20:39:02 +01:00
peer - > mn = peer_mn ;
2016-08-12 15:07:33 +02:00
} else if ( ! ieee802_1x_kay_create_potential_peer (
participant , peer_mi - > mi , peer_mn ) ) {
return - 1 ;
2014-03-25 20:39:02 +01:00
}
}
return 0 ;
}
/**
* ieee802_1x_mka_decode_potential_peer_body -
*/
static int
ieee802_1x_mka_decode_potential_peer_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * peer_msg , size_t msg_len )
{
2016-08-12 15:07:33 +02:00
const struct ieee802_1x_mka_hdr * hdr ;
2014-03-25 20:39:02 +01:00
size_t body_len ;
size_t i ;
2016-08-12 15:07:33 +02:00
hdr = ( const struct ieee802_1x_mka_hdr * ) peer_msg ;
2014-03-25 20:39:02 +01:00
body_len = get_mka_param_body_len ( hdr ) ;
2016-07-19 11:56:57 +02:00
if ( body_len % 16 ! = 0 ) {
wpa_printf ( MSG_ERROR ,
" KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets " ,
body_len ) ;
return - 1 ;
}
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:33 +02:00
for ( i = 0 ; i < body_len ; i + = sizeof ( struct ieee802_1x_mka_peer_id ) ) {
const struct ieee802_1x_mka_peer_id * peer_mi ;
u32 peer_mn ;
peer_mi = ( struct ieee802_1x_mka_peer_id * )
( peer_msg + MKA_HDR_LEN + i ) ;
peer_mn = be_to_host32 ( peer_mi - > mn ) ;
2014-03-25 20:39:02 +01:00
/* it is myself */
if ( os_memcmp ( peer_mi , participant - > mi , MI_LEN ) = = 0 ) {
/* My message id is used by other participant */
2016-08-12 15:07:33 +02:00
if ( peer_mn > participant - > mn & &
! reset_participant_mi ( participant ) )
wpa_printf ( MSG_DEBUG , " KaY: Could not update mi " ) ;
2014-03-25 20:39:02 +01:00
continue ;
}
}
return 0 ;
}
/**
* ieee802_1x_mka_sak_use_body_present
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_sak_use_body_present (
struct ieee802_1x_mka_participant * participant )
{
2016-08-12 15:07:33 +02:00
return participant - > to_use_sak ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_get_sak_use_length
*/
static int
ieee802_1x_mka_get_sak_use_length (
struct ieee802_1x_mka_participant * participant )
{
int length = MKA_HDR_LEN ;
if ( participant - > kay - > macsec_desired & & participant - > advised_desired )
length = sizeof ( struct ieee802_1x_mka_sak_use_body ) ;
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( length ) ;
2014-03-25 20:39:02 +01:00
}
/**
2018-02-20 20:28:39 +01:00
* ieee802_1x_mka_get_lpn
2014-03-25 20:39:02 +01:00
*/
static u32
ieee802_1x_mka_get_lpn ( struct ieee802_1x_mka_participant * principal ,
struct ieee802_1x_mka_ki * ki )
{
2018-02-20 20:28:39 +01:00
struct transmit_sa * txsa ;
2014-03-25 20:39:02 +01:00
u32 lpn = 0 ;
2018-02-20 20:28:39 +01:00
dl_list_for_each ( txsa , & principal - > txsc - > sa_list ,
struct transmit_sa , list ) {
if ( is_ki_equal ( & txsa - > pkey - > key_identifier , ki ) ) {
/* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
* MKA to communicate the lowest PN used for
* transmission with the SAK within the last two
* seconds " . Achieve this 2 second delay by setting the
* lpn using the transmit next PN ( i . e . , txsa - > next_pn )
* that was read last time here ( i . e . , mka_hello_time
* 2 seconds ago ) .
*
* The lowest acceptable PN is the same as the last
* transmitted PN , which is one less than the next
* transmit PN .
*
* NOTE : This method only works if mka_hello_time is 2 s .
*/
lpn = ( txsa - > next_pn > 0 ) ? ( txsa - > next_pn - 1 ) : 0 ;
/* Now read the current transmit next PN for use next
* time through . */
secy_get_transmit_next_pn ( principal - > kay , txsa ) ;
break ;
2014-03-25 20:39:02 +01:00
}
}
if ( lpn = = 0 )
lpn = 1 ;
return lpn ;
}
/**
* ieee802_1x_mka_encode_sak_use_body -
*/
static int
ieee802_1x_mka_encode_sak_use_body (
struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_sak_use_body * body ;
2016-08-15 11:43:42 +02:00
struct ieee802_1x_kay * kay = participant - > kay ;
2014-03-25 20:39:02 +01:00
unsigned int length ;
2019-08-27 05:55:37 +02:00
u32 olpn , llpn ;
2014-03-25 20:39:02 +01:00
length = ieee802_1x_mka_get_sak_use_length ( participant ) ;
2016-07-19 11:56:53 +02:00
body = wpabuf_put ( buf , length ) ;
2014-03-25 20:39:02 +01:00
body - > type = MKA_SAK_USE ;
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
if ( length = = MKA_HDR_LEN ) {
2020-04-24 00:27:57 +02:00
body - > ptx = true ;
body - > prx = true ;
2014-03-25 20:39:02 +01:00
body - > lan = 0 ;
2020-04-24 00:27:57 +02:00
body - > lrx = false ;
body - > ltx = false ;
body - > delay_protect = false ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
2018-11-02 19:02:14 +01:00
/* data delay protect */
2018-02-20 20:28:38 +01:00
body - > delay_protect = kay - > mka_hello_time < = MKA_BOUNDED_HELLO_TIME ;
2019-08-27 05:55:37 +02:00
/* lowest accept packet numbers */
olpn = ieee802_1x_mka_get_lpn ( participant , & participant - > oki ) ;
body - > olpn = host_to_be32 ( olpn ) ;
llpn = ieee802_1x_mka_get_lpn ( participant , & participant - > lki ) ;
body - > llpn = host_to_be32 ( llpn ) ;
if ( participant - > is_key_server ) {
/* The CP will spend most of it's time in RETIRE where only
* the old key is populated . Therefore we should be checking
* the OLPN most of the time .
*/
if ( participant - > lrx ) {
if ( llpn > kay - > pn_exhaustion ) {
wpa_printf ( MSG_WARNING ,
" KaY: My LLPN exhaustion " ) ;
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
2019-08-27 05:55:37 +02:00
}
} else {
if ( olpn > kay - > pn_exhaustion ) {
wpa_printf ( MSG_WARNING ,
" KaY: My OLPN exhaustion " ) ;
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
2019-08-27 05:55:37 +02:00
}
}
2014-03-25 20:39:02 +01:00
}
/* plain tx, plain rx */
2016-08-15 11:43:42 +02:00
body - > ptx = ! kay - > macsec_protect ;
body - > prx = kay - > macsec_validate ! = Strict ;
2014-03-25 20:39:02 +01:00
/* latest key: rx, tx, key server member identifier key number */
body - > lan = participant - > lan ;
2016-08-15 11:43:42 +02:00
os_memcpy ( body - > lsrv_mi , participant - > lki . mi , sizeof ( body - > lsrv_mi ) ) ;
2014-03-25 20:39:02 +01:00
body - > lkn = host_to_be32 ( participant - > lki . kn ) ;
body - > lrx = participant - > lrx ;
body - > ltx = participant - > ltx ;
/* old key: rx, tx, key server member identifier key number */
body - > oan = participant - > oan ;
if ( participant - > oki . kn ! = participant - > lki . kn & &
participant - > oki . kn ! = 0 ) {
2020-04-24 00:27:57 +02:00
body - > otx = true ;
body - > orx = true ;
2014-03-25 20:39:02 +01:00
os_memcpy ( body - > osrv_mi , participant - > oki . mi ,
sizeof ( body - > osrv_mi ) ) ;
body - > okn = host_to_be32 ( participant - > oki . kn ) ;
} else {
2020-04-24 00:27:57 +02:00
body - > otx = false ;
body - > orx = false ;
2014-03-25 20:39:02 +01:00
}
/* set CP's variable */
if ( body - > ltx ) {
2020-04-24 00:27:57 +02:00
kay - > tx_enable = true ;
kay - > port_enable = true ;
2014-03-25 20:39:02 +01:00
}
2016-08-15 11:43:42 +02:00
if ( body - > lrx )
2020-04-24 00:27:57 +02:00
kay - > rx_enable = true ;
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_dump_sak_use_body ( body ) ;
return 0 ;
}
/**
* ieee802_1x_mka_decode_sak_use_body -
*/
static int
ieee802_1x_mka_decode_sak_use_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
struct ieee802_1x_mka_hdr * hdr ;
struct ieee802_1x_mka_sak_use_body * body ;
struct ieee802_1x_kay_peer * peer ;
struct data_key * sa_key = NULL ;
size_t body_len ;
struct ieee802_1x_mka_ki ki ;
u32 lpn ;
2016-08-15 11:43:42 +02:00
struct ieee802_1x_kay * kay = participant - > kay ;
2019-08-27 05:55:38 +02:00
u32 olpn , llpn ;
2014-03-25 20:39:02 +01:00
if ( ! participant - > principal ) {
wpa_printf ( MSG_WARNING , " KaY: Participant is not principal " ) ;
return - 1 ;
}
peer = ieee802_1x_kay_get_live_peer ( participant ,
participant - > current_peer_id . mi ) ;
if ( ! peer ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_WARNING ,
" KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set " ,
mi_txt ( participant - > current_peer_id . mi ) ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
hdr = ( struct ieee802_1x_mka_hdr * ) mka_msg ;
body_len = get_mka_param_body_len ( hdr ) ;
body = ( struct ieee802_1x_mka_sak_use_body * ) mka_msg ;
ieee802_1x_mka_dump_sak_use_body ( body ) ;
if ( ( body_len ! = 0 ) & & ( body_len < 40 ) ) {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets " ,
body_len ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
/* TODO: what action should I take when peer does not support MACsec */
if ( body_len = = 0 ) {
wpa_printf ( MSG_WARNING , " KaY: Peer does not support MACsec " ) ;
return 0 ;
}
/* TODO: when the plain tx or rx of peer is true, should I change
* the attribute of controlled port
*/
if ( body - > prx )
wpa_printf ( MSG_WARNING , " KaY: peer's plain rx are TRUE " ) ;
if ( body - > ptx )
wpa_printf ( MSG_WARNING , " KaY: peer's plain tx are TRUE " ) ;
/* TODO: how to set the MACsec hardware when delay_protect is true */
2016-06-24 00:38:48 +02:00
if ( body - > delay_protect & &
( ! be_to_host32 ( body - > llpn ) | | ! be_to_host32 ( body - > olpn ) ) ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING ,
2018-06-15 17:06:41 +02:00
" KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2019-08-27 05:55:38 +02:00
olpn = be_to_host32 ( body - > olpn ) ;
llpn = be_to_host32 ( body - > llpn ) ;
/* Our most recent distributed key should be the first in the list.
* If it doesn ' t exist then we can ' t really do anything .
* Be lenient and don ' t return error here as there are legitimate cases
* where this can happen such as when a new participant joins the CA and
* the first frame it receives can have a SAKuse but not distSAK .
*/
sa_key = dl_list_first ( & participant - > sak_list , struct data_key , list ) ;
if ( ! sa_key ) {
wpa_printf ( MSG_INFO ,
" KaY: We don't have a latest distributed key - ignore SAK use " ) ;
return 0 ;
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
/* The peer's most recent key will be the "latest key" if it is present
* otherwise it will be the " old key " if in the RETIRE state .
*/
if ( body - > lrx ) {
os_memcpy ( ki . mi , body - > lsrv_mi , sizeof ( ki . mi ) ) ;
ki . kn = be_to_host32 ( body - > lkn ) ;
lpn = llpn ;
} else {
os_memcpy ( ki . mi , body - > osrv_mi , sizeof ( ki . mi ) ) ;
ki . kn = be_to_host32 ( body - > okn ) ;
lpn = olpn ;
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
/* If the most recent distributed keys don't agree then someone is out
* of sync . Perhaps non key server hasn ' t processed the most recent
* distSAK yet and the key server is processing an old packet after it
* has done distSAK . Be lenient and don ' t return error in this
* particular case ; otherwise , the key server will reset its MI and
* cause a traffic disruption which is really undesired for a simple
* timing issue .
*/
if ( ! is_ki_equal ( & sa_key - > key_identifier , & ki ) ) {
wpa_printf ( MSG_INFO ,
" KaY: Distributed keys don't match - ignore SAK use " ) ;
return 0 ;
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
sa_key - > next_pn = lpn ;
2014-03-25 20:39:02 +01:00
2019-08-27 05:55:38 +02:00
/* The key server must check that all peers are using the most recent
* distributed key . Non key servers must check if the key server is
* transmitting .
*/
if ( participant - > is_key_server ) {
struct ieee802_1x_kay_peer * peer_iter ;
2020-04-24 00:27:57 +02:00
bool all_receiving = true ;
2019-08-27 05:55:38 +02:00
/* Distributed keys are equal from above comparison. */
2020-04-24 00:27:57 +02:00
peer - > sak_used = true ;
2019-08-27 05:55:38 +02:00
dl_list_for_each ( peer_iter , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( ! peer_iter - > sak_used ) {
2020-04-24 00:27:57 +02:00
all_receiving = false ;
2018-02-20 20:28:39 +01:00
break ;
}
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
if ( all_receiving ) {
2020-04-24 00:27:57 +02:00
participant - > to_dist_sak = false ;
ieee802_1x_cp_set_allreceiving ( kay - > cp , true ) ;
2019-08-27 05:55:38 +02:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
}
} else if ( peer - > is_key_server ) {
if ( body - > ltx ) {
2020-04-24 00:27:57 +02:00
ieee802_1x_cp_set_servertransmitting ( kay - > cp , true ) ;
2019-08-27 05:55:38 +02:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
}
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
/* If I'm key server, and detects peer member PN exhaustion, rekey.
* We only need to check the PN of the most recent distributed key . This
* could be the peer ' s " latest " or " old " key depending on its current
* state . If both " old " and " latest " keys are present then the " old " key
* has already been exhausted .
*/
if ( participant - > is_key_server & & lpn > kay - > pn_exhaustion ) {
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
2019-08-27 05:55:38 +02:00
wpa_printf ( MSG_WARNING , " KaY: Peer LPN exhaustion " ) ;
2014-03-25 20:39:02 +01:00
}
2019-08-27 05:55:38 +02:00
/* Get the associated RX SAs of the keys for delay protection since both
* can be in use . Delay protect window ( communicated via MKA ) is tighter
* than SecY ' s current replay protect window , so tell SecY the new ( and
* higher ) lpn .
*/
2018-02-20 20:28:39 +01:00
if ( body - > delay_protect ) {
2019-08-27 05:55:38 +02:00
struct receive_sc * rxsc ;
struct receive_sa * rxsa ;
2020-04-24 00:27:57 +02:00
bool found = false ;
2019-08-27 05:55:38 +02:00
dl_list_for_each ( rxsc , & participant - > rxsc_list ,
struct receive_sc , list ) {
dl_list_for_each ( rxsa , & rxsc - > sa_list ,
struct receive_sa , list ) {
if ( sa_key & & rxsa - > pkey = = sa_key ) {
2020-04-24 00:27:57 +02:00
found = true ;
2019-08-27 05:55:38 +02:00
break ;
}
}
if ( found )
break ;
2018-02-20 20:28:39 +01:00
}
2019-08-27 05:55:38 +02:00
if ( found ) {
secy_get_receive_lowest_pn ( participant - > kay , rxsa ) ;
if ( lpn > rxsa - > lowest_pn ) {
rxsa - > lowest_pn = lpn ;
secy_set_receive_lowest_pn ( participant - > kay ,
rxsa ) ;
wpa_printf ( MSG_DEBUG ,
" KaY: update dist LPN=0x%x " , lpn ) ;
}
}
/* FIX: Delay protection for the SA being replaced is not
* implemented . Note that this key will be active for at least
* MKA_SAK_RETIRE_TIME ( 3 seconds ) but could be longer depending
* on how long it takes to get from RECEIVE to TRANSMITTING or
* if going via ABANDON . Delay protection does allow PNs within
* a 2 second window , so getting PN would be a lot of work for
* just 1 second ' s worth of protection .
*/
2014-03-25 20:39:02 +01:00
}
return 0 ;
}
/**
* ieee802_1x_mka_dist_sak_body_present
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_dist_sak_body_present (
struct ieee802_1x_mka_participant * participant )
{
2018-12-29 00:27:22 +01:00
return participant - > is_key_server & & participant - > to_dist_sak & &
participant - > new_key ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_get_dist_sak_length
*/
static int
ieee802_1x_mka_get_dist_sak_length (
struct ieee802_1x_mka_participant * participant )
{
2016-08-12 15:07:33 +02:00
int length = MKA_HDR_LEN ;
2016-08-22 20:02:40 +02:00
unsigned int cs_index = participant - > kay - > macsec_csindex ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:35 +02:00
if ( participant - > advised_desired & & cs_index < CS_TABLE_SIZE ) {
2014-03-25 20:39:02 +01:00
length = sizeof ( struct ieee802_1x_mka_dist_sak_body ) ;
if ( cs_index ! = DEFAULT_CS_INDEX )
length + = CS_ID_LEN ;
length + = cipher_suite_tbl [ cs_index ] . sak_len + 8 ;
}
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( length ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_encode_dist_sak_body -
*/
static int
ieee802_1x_mka_encode_dist_sak_body (
struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_dist_sak_body * body ;
struct data_key * sak ;
unsigned int length ;
2016-08-22 20:02:40 +02:00
unsigned int cs_index ;
2014-03-25 20:39:02 +01:00
int sak_pos ;
length = ieee802_1x_mka_get_dist_sak_length ( participant ) ;
body = wpabuf_put ( buf , length ) ;
body - > type = MKA_DISTRIBUTED_SAK ;
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
if ( length = = MKA_HDR_LEN ) {
body - > confid_offset = 0 ;
body - > dan = 0 ;
return 0 ;
}
sak = participant - > new_key ;
2018-12-27 23:47:53 +01:00
if ( ! sak ) {
wpa_printf ( MSG_DEBUG ,
" KaY: No SAK available to build Distributed SAK parameter set " ) ;
return - 1 ;
}
2014-03-25 20:39:02 +01:00
body - > confid_offset = sak - > confidentiality_offset ;
body - > dan = sak - > an ;
body - > kn = host_to_be32 ( sak - > key_identifier . kn ) ;
cs_index = participant - > kay - > macsec_csindex ;
sak_pos = 0 ;
2016-08-12 15:07:35 +02:00
if ( cs_index > = CS_TABLE_SIZE )
return - 1 ;
2014-03-25 20:39:02 +01:00
if ( cs_index ! = DEFAULT_CS_INDEX ) {
2016-08-12 15:07:35 +02:00
be64 cs ;
cs = host_to_be64 ( cipher_suite_tbl [ cs_index ] . id ) ;
os_memcpy ( body - > sak , & cs , CS_ID_LEN ) ;
2014-03-25 20:39:02 +01:00
sak_pos = CS_ID_LEN ;
}
2018-12-26 11:35:18 +01:00
if ( aes_wrap ( participant - > kek . key , participant - > kek . len ,
2014-03-25 20:39:02 +01:00
cipher_suite_tbl [ cs_index ] . sak_len / 8 ,
sak - > key , body - > sak + sak_pos ) ) {
wpa_printf ( MSG_ERROR , " KaY: AES wrap failed " ) ;
return - 1 ;
}
ieee802_1x_mka_dump_dist_sak_body ( body ) ;
return 0 ;
}
/**
* ieee802_1x_kay_init_data_key -
*/
2016-08-12 15:07:35 +02:00
static void ieee802_1x_kay_init_data_key ( struct data_key * pkey )
2014-03-25 20:39:02 +01:00
{
2020-04-24 00:27:57 +02:00
pkey - > transmits = true ;
pkey - > receives = true ;
2014-03-25 20:39:02 +01:00
os_get_time ( & pkey - > created_time ) ;
2018-11-02 19:02:17 +01:00
pkey - > next_pn = 1 ;
2014-03-25 20:39:02 +01:00
pkey - > user = 1 ;
}
/**
* ieee802_1x_kay_decode_dist_sak_body -
*/
static int
ieee802_1x_mka_decode_dist_sak_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
struct ieee802_1x_mka_hdr * hdr ;
struct ieee802_1x_mka_dist_sak_body * body ;
struct ieee802_1x_kay_peer * peer ;
struct macsec_ciphersuite * cs ;
size_t body_len ;
struct data_key * sa_key = NULL ;
int sak_len ;
u8 * wrap_sak ;
u8 * unwrap_sak ;
2016-08-15 11:43:42 +02:00
struct ieee802_1x_kay * kay = participant - > kay ;
2014-03-25 20:39:02 +01:00
hdr = ( struct ieee802_1x_mka_hdr * ) mka_msg ;
body_len = get_mka_param_body_len ( hdr ) ;
if ( ( body_len ! = 0 ) & & ( body_len ! = 28 ) & & ( body_len < 36 ) ) {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets " ,
body_len ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
if ( ! participant - > principal ) {
wpa_printf ( MSG_ERROR ,
" KaY: I can't accept the distributed SAK as I am not principal " ) ;
return - 1 ;
}
if ( participant - > is_key_server ) {
wpa_printf ( MSG_ERROR ,
2018-12-27 15:19:46 +01:00
" KaY: Reject distributed SAK since I'm a key server " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2016-08-15 11:43:42 +02:00
if ( ! kay - > macsec_desired | |
kay - > macsec_capable = = MACSEC_CAP_NOT_IMPLEMENTED ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR ,
" KaY: I am not MACsec-desired or without MACsec capable " ) ;
return - 1 ;
}
peer = ieee802_1x_kay_get_live_peer ( participant ,
participant - > current_peer_id . mi ) ;
if ( ! peer ) {
wpa_printf ( MSG_ERROR ,
" KaY: The key server is not in my live peers list " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
if ( ! sci_equal ( & kay - > key_server_sci , & peer - > sci ) ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR , " KaY: The key server is not elected " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
2014-03-25 20:39:02 +01:00
if ( body_len = = 0 ) {
2020-04-24 00:27:57 +02:00
kay - > authenticated = true ;
kay - > secured = false ;
kay - > failed = false ;
participant - > advised_desired = false ;
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_connect_authenticated ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2018-06-15 17:06:41 +02:00
wpa_printf ( MSG_WARNING , " KaY: The Key server advise no MACsec " ) ;
2020-04-24 00:27:57 +02:00
participant - > to_use_sak = false ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
2016-08-15 11:43:42 +02:00
2020-04-24 00:27:57 +02:00
participant - > advised_desired = true ;
kay - > authenticated = false ;
kay - > secured = true ;
kay - > failed = false ;
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_connect_secure ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2014-03-25 20:39:02 +01:00
body = ( struct ieee802_1x_mka_dist_sak_body * ) mka_msg ;
ieee802_1x_mka_dump_dist_sak_body ( body ) ;
dl_list_for_each ( sa_key , & participant - > sak_list , struct data_key , list )
{
if ( os_memcmp ( sa_key - > key_identifier . mi ,
participant - > current_peer_id . mi , MI_LEN ) = = 0 & &
sa_key - > key_identifier . kn = = be_to_host32 ( body - > kn ) ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: SAK has already been installed - do not set it again " ) ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
}
2016-08-15 11:43:42 +02:00
2014-03-25 20:39:02 +01:00
if ( body_len = = 28 ) {
sak_len = DEFAULT_SA_KEY_LEN ;
wrap_sak = body - > sak ;
2016-08-15 11:43:42 +02:00
kay - > macsec_csindex = DEFAULT_CS_INDEX ;
2016-08-12 15:07:35 +02:00
cs = & cipher_suite_tbl [ kay - > macsec_csindex ] ;
2014-03-25 20:39:02 +01:00
} else {
2018-12-27 15:37:57 +01:00
unsigned int idx ;
cs = ieee802_1x_kay_get_cipher_suite ( participant , body - > sak ,
& idx ) ;
2014-03-25 20:39:02 +01:00
if ( ! cs ) {
wpa_printf ( MSG_ERROR ,
" KaY: I can't support the Cipher Suite advised by key server " ) ;
return - 1 ;
}
sak_len = cs - > sak_len ;
wrap_sak = body - > sak + CS_ID_LEN ;
2018-12-27 15:37:57 +01:00
kay - > macsec_csindex = idx ;
2014-03-25 20:39:02 +01:00
}
unwrap_sak = os_zalloc ( sak_len ) ;
if ( ! unwrap_sak ) {
wpa_printf ( MSG_ERROR , " KaY-%s: Out of memory " , __func__ ) ;
return - 1 ;
}
2018-12-26 11:35:18 +01:00
if ( aes_unwrap ( participant - > kek . key , participant - > kek . len ,
sak_len > > 3 , wrap_sak , unwrap_sak ) ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR , " KaY: AES unwrap failed " ) ;
os_free ( unwrap_sak ) ;
return - 1 ;
}
2018-06-15 17:06:41 +02:00
wpa_hexdump_key ( MSG_DEBUG , " \t AES Key Unwrap of SAK.: " ,
2018-02-20 20:28:40 +01:00
unwrap_sak , sak_len ) ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:35 +02:00
sa_key = os_zalloc ( sizeof ( * sa_key ) ) ;
if ( ! sa_key ) {
2014-03-25 20:39:02 +01:00
os_free ( unwrap_sak ) ;
return - 1 ;
}
2016-08-12 15:07:35 +02:00
os_memcpy ( & sa_key - > key_identifier . mi , & participant - > current_peer_id . mi ,
MI_LEN ) ;
sa_key - > key_identifier . kn = be_to_host32 ( body - > kn ) ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:35 +02:00
sa_key - > key = unwrap_sak ;
sa_key - > key_len = sak_len ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:35 +02:00
sa_key - > confidentiality_offset = body - > confid_offset ;
sa_key - > an = body - > dan ;
ieee802_1x_kay_init_data_key ( sa_key ) ;
2014-03-25 20:39:02 +01:00
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_use_data_key ( sa_key ) ;
2014-03-25 20:39:02 +01:00
dl_list_add ( & participant - > sak_list , & sa_key - > list ) ;
2016-08-12 15:07:35 +02:00
ieee802_1x_cp_set_ciphersuite ( kay - > cp , cs - > id ) ;
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
ieee802_1x_cp_set_offset ( kay - > cp , body - > confid_offset ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2016-08-12 15:07:35 +02:00
ieee802_1x_cp_set_distributedki ( kay - > cp , & sa_key - > key_identifier ) ;
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_set_distributedan ( kay - > cp , body - > dan ) ;
ieee802_1x_cp_signal_newsak ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2014-03-25 20:39:02 +01:00
2016-12-15 21:10:53 +01:00
kay - > rcvd_keys + + ;
2020-04-24 00:27:57 +02:00
participant - > to_use_sak = true ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
/**
* ieee802_1x_mka_icv_body_present
*/
2020-04-24 00:27:57 +02:00
static bool
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_icv_body_present ( struct ieee802_1x_mka_participant * participant )
{
2020-04-24 00:27:57 +02:00
return true ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_get_icv_length
*/
static int
ieee802_1x_mka_get_icv_length ( struct ieee802_1x_mka_participant * participant )
{
int length ;
2017-04-07 23:39:23 +02:00
/* Determine if we need space for the ICV Indicator */
if ( mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_len ! =
DEFAULT_ICV_LEN )
length = sizeof ( struct ieee802_1x_mka_icv_body ) ;
else
length = 0 ;
2014-03-25 20:39:02 +01:00
length + = mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_len ;
2016-08-12 15:07:33 +02:00
return MKA_ALIGN_LENGTH ( length ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_encode_icv_body -
*/
static int
ieee802_1x_mka_encode_icv_body ( struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf )
{
struct ieee802_1x_mka_icv_body * body ;
unsigned int length ;
u8 cmac [ MAX_ICV_LEN ] ;
length = ieee802_1x_mka_get_icv_length ( participant ) ;
2017-04-07 23:39:23 +02:00
if ( mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_len ! =
DEFAULT_ICV_LEN ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: ICV Indicator " ) ;
2014-03-25 20:39:02 +01:00
body = wpabuf_put ( buf , MKA_HDR_LEN ) ;
body - > type = MKA_ICV_INDICATOR ;
2017-04-07 23:39:23 +02:00
length - = MKA_HDR_LEN ;
set_mka_param_body_len ( body , length ) ;
2014-03-25 20:39:02 +01:00
}
if ( mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_hash (
2018-12-26 15:18:00 +01:00
participant - > ick . key , participant - > ick . len ,
wpabuf_head ( buf ) , wpabuf_len ( buf ) , cmac ) ) {
wpa_printf ( MSG_ERROR , " KaY: failed to calculate ICV " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_DEBUG , " KaY: ICV " , cmac , length ) ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:33 +02:00
os_memcpy ( wpabuf_put ( buf , length ) , cmac , length ) ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
/**
* ieee802_1x_mka_decode_icv_body -
*/
2018-12-27 15:19:46 +01:00
static const u8 *
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_decode_icv_body ( struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
2018-12-27 15:19:46 +01:00
const struct ieee802_1x_mka_hdr * hdr ;
const struct ieee802_1x_mka_icv_body * body ;
2014-03-25 20:39:02 +01:00
size_t body_len ;
size_t left_len ;
2016-08-07 10:40:55 +02:00
u8 body_type ;
2014-03-25 20:39:02 +01:00
const u8 * pos ;
pos = mka_msg ;
left_len = msg_len ;
2018-12-27 15:19:46 +01:00
while ( left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN ) {
hdr = ( const struct ieee802_1x_mka_hdr * ) pos ;
2017-08-17 23:51:58 +02:00
body_len = MKA_ALIGN_LENGTH ( get_mka_param_body_len ( hdr ) ) ;
2014-03-25 20:39:02 +01:00
body_type = get_mka_param_body_type ( hdr ) ;
2018-12-27 15:19:46 +01:00
if ( left_len < body_len + MKA_HDR_LEN )
2014-03-25 20:39:02 +01:00
break ;
if ( body_type ! = MKA_ICV_INDICATOR ) {
left_len - = MKA_HDR_LEN + body_len ;
pos + = MKA_HDR_LEN + body_len ;
continue ;
}
2018-12-27 15:19:46 +01:00
body = ( const struct ieee802_1x_mka_icv_body * ) pos ;
2014-03-25 20:39:02 +01:00
if ( body_len
2018-12-27 15:19:46 +01:00
< mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_len )
2014-03-25 20:39:02 +01:00
return NULL ;
return body - > icv ;
}
2018-12-27 15:19:46 +01:00
return mka_msg + msg_len - DEFAULT_ICV_LEN ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_mka_decode_dist_cak_body -
*/
static int
ieee802_1x_mka_decode_dist_cak_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
struct ieee802_1x_mka_hdr * hdr ;
size_t body_len ;
hdr = ( struct ieee802_1x_mka_hdr * ) mka_msg ;
body_len = get_mka_param_body_len ( hdr ) ;
if ( body_len < 28 ) {
wpa_printf ( MSG_ERROR ,
2018-06-15 17:06:41 +02:00
" KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets " ,
2016-08-12 15:07:33 +02:00
body_len ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
return 0 ;
}
/**
* ieee802_1x_mka_decode_kmd_body -
*/
static int
ieee802_1x_mka_decode_kmd_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
struct ieee802_1x_mka_hdr * hdr ;
size_t body_len ;
hdr = ( struct ieee802_1x_mka_hdr * ) mka_msg ;
body_len = get_mka_param_body_len ( hdr ) ;
if ( body_len < 5 ) {
wpa_printf ( MSG_ERROR ,
2018-06-15 17:06:41 +02:00
" KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets " ,
2016-08-12 15:07:33 +02:00
body_len ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
return 0 ;
}
/**
* ieee802_1x_mka_decode_announce_body -
*/
static int ieee802_1x_mka_decode_announce_body (
struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len )
{
return 0 ;
}
2016-08-15 11:43:42 +02:00
struct mka_param_body_handler {
int ( * body_tx ) ( struct ieee802_1x_mka_participant * participant ,
struct wpabuf * buf ) ;
int ( * body_rx ) ( struct ieee802_1x_mka_participant * participant ,
const u8 * mka_msg , size_t msg_len ) ;
int ( * body_length ) ( struct ieee802_1x_mka_participant * participant ) ;
2020-04-24 00:27:57 +02:00
bool ( * body_present ) ( struct ieee802_1x_mka_participant * participant ) ;
2016-08-15 11:43:42 +02:00
} ;
2016-08-15 11:43:41 +02:00
static struct mka_param_body_handler mka_body_handler [ ] = {
2018-12-27 15:19:46 +01:00
/* Basic parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_basic_body ,
. body_rx = NULL ,
. body_length = ieee802_1x_mka_basic_body_length ,
. body_present = ieee802_1x_mka_basic_body_present
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* Live Peer List parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_live_peer_body ,
. body_rx = ieee802_1x_mka_decode_live_peer_body ,
. body_length = ieee802_1x_mka_get_live_peer_length ,
. body_present = ieee802_1x_mka_live_peer_body_present
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* Potential Peer List parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_potential_peer_body ,
. body_rx = ieee802_1x_mka_decode_potential_peer_body ,
. body_length = ieee802_1x_mka_get_potential_peer_length ,
. body_present = ieee802_1x_mka_potential_peer_body_present
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* MACsec SAK Use parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_sak_use_body ,
. body_rx = ieee802_1x_mka_decode_sak_use_body ,
. body_length = ieee802_1x_mka_get_sak_use_length ,
. body_present = ieee802_1x_mka_sak_use_body_present
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* Distributed SAK parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_dist_sak_body ,
. body_rx = ieee802_1x_mka_decode_dist_sak_body ,
. body_length = ieee802_1x_mka_get_dist_sak_length ,
. body_present = ieee802_1x_mka_dist_sak_body_present
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* Distribute CAK parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = NULL ,
. body_rx = ieee802_1x_mka_decode_dist_cak_body ,
. body_length = NULL ,
. body_present = NULL
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* KMD parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = NULL ,
. body_rx = ieee802_1x_mka_decode_kmd_body ,
. body_length = NULL ,
. body_present = NULL
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* Announcement parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = NULL ,
. body_rx = ieee802_1x_mka_decode_announce_body ,
. body_length = NULL ,
. body_present = NULL
2014-03-25 20:39:02 +01:00
} ,
2018-12-27 15:19:46 +01:00
/* ICV Indicator parameter set */
2014-03-25 20:39:02 +01:00
{
2016-08-15 11:43:42 +02:00
. body_tx = ieee802_1x_mka_encode_icv_body ,
. body_rx = NULL ,
. body_length = ieee802_1x_mka_get_icv_length ,
. body_present = ieee802_1x_mka_icv_body_present
2014-03-25 20:39:02 +01:00
} ,
} ;
/**
2016-10-21 14:45:29 +02:00
* ieee802_1x_kay_use_data_key - Take reference on a key
*/
static void ieee802_1x_kay_use_data_key ( struct data_key * pkey )
{
pkey - > user + + ;
}
/**
* ieee802_1x_kay_deinit_data_key - Release reference on a key and
* free if there are no remaining users
2014-03-25 20:39:02 +01:00
*/
2016-06-24 00:40:24 +02:00
static void ieee802_1x_kay_deinit_data_key ( struct data_key * pkey )
2014-03-25 20:39:02 +01:00
{
if ( ! pkey )
return ;
pkey - > user - - ;
if ( pkey - > user > 1 )
return ;
os_free ( pkey - > key ) ;
os_free ( pkey ) ;
}
/**
* ieee802_1x_kay_generate_new_sak -
*/
static int
ieee802_1x_kay_generate_new_sak ( struct ieee802_1x_mka_participant * participant )
{
struct data_key * sa_key = NULL ;
struct ieee802_1x_kay_peer * peer ;
struct ieee802_1x_kay * kay = participant - > kay ;
int ctx_len , ctx_offset ;
u8 * context ;
2016-08-12 15:07:35 +02:00
unsigned int key_len ;
u8 * key ;
struct macsec_ciphersuite * cs ;
2014-03-25 20:39:02 +01:00
/* check condition for generating a fresh SAK:
* must have one live peer
* and MKA life time elapse since last distribution
* or potential peer is empty
*/
if ( dl_list_empty ( & participant - > live_peers ) ) {
wpa_printf ( MSG_ERROR ,
2018-06-15 17:06:41 +02:00
" KaY: Live peers list must not be empty when generating fresh SAK " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
/* FIXME: A fresh SAK not generated until
* the live peer list contains at least one peer and
* MKA life time has elapsed since the prior SAK was first distributed ,
* or the Key server ' s potential peer is empty
* but I can ' t understand the second item , so
* here only check first item and ingore
* & & ( ! dl_list_empty ( & participant - > potential_peers ) ) ) {
*/
if ( ( time ( NULL ) - kay - > dist_time ) < MKA_LIFE_TIME / 1000 ) {
wpa_printf ( MSG_ERROR ,
2018-06-15 17:06:41 +02:00
" KaY: Life time has not elapsed since prior SAK distributed " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2016-08-12 15:07:35 +02:00
cs = & cipher_suite_tbl [ kay - > macsec_csindex ] ;
key_len = cs - > sak_len ;
key = os_zalloc ( key_len ) ;
if ( ! key ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR , " KaY-%s: Out of memory " , __func__ ) ;
return - 1 ;
}
2016-08-12 15:07:35 +02:00
ctx_len = key_len + sizeof ( kay - > dist_kn ) ;
2014-03-25 20:39:02 +01:00
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list )
ctx_len + = sizeof ( peer - > mi ) ;
ctx_len + = sizeof ( participant - > mi ) ;
context = os_zalloc ( ctx_len ) ;
2016-08-12 15:07:35 +02:00
if ( ! context )
goto fail ;
2014-03-25 20:39:02 +01:00
ctx_offset = 0 ;
2016-08-12 15:07:35 +02:00
if ( os_get_random ( context + ctx_offset , key_len ) < 0 )
goto fail ;
ctx_offset + = key_len ;
2014-03-25 20:39:02 +01:00
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
os_memcpy ( context + ctx_offset , peer - > mi , sizeof ( peer - > mi ) ) ;
ctx_offset + = sizeof ( peer - > mi ) ;
}
os_memcpy ( context + ctx_offset , participant - > mi ,
sizeof ( participant - > mi ) ) ;
ctx_offset + = sizeof ( participant - > mi ) ;
os_memcpy ( context + ctx_offset , & kay - > dist_kn , sizeof ( kay - > dist_kn ) ) ;
2018-11-02 19:02:18 +01:00
if ( key_len = = 16 | | key_len = = 32 ) {
2018-12-26 11:20:57 +01:00
if ( ieee802_1x_sak_aes_cmac ( participant - > cak . key ,
participant - > cak . len ,
context , ctx_len ,
key , key_len ) ) {
2018-11-02 19:02:18 +01:00
wpa_printf ( MSG_ERROR , " KaY: Failed to generate SAK " ) ;
goto fail ;
}
2014-03-25 20:39:02 +01:00
} else {
2018-06-15 17:06:41 +02:00
wpa_printf ( MSG_ERROR , " KaY: SAK Length(%u) not supported " ,
key_len ) ;
2016-08-12 15:07:35 +02:00
goto fail ;
2014-03-25 20:39:02 +01:00
}
2018-02-20 20:28:40 +01:00
wpa_hexdump_key ( MSG_DEBUG , " KaY: generated new SAK " , key , key_len ) ;
2016-08-12 15:07:35 +02:00
os_free ( context ) ;
context = NULL ;
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:35 +02:00
sa_key = os_zalloc ( sizeof ( * sa_key ) ) ;
2014-03-25 20:39:02 +01:00
if ( ! sa_key ) {
2016-08-12 15:07:35 +02:00
wpa_printf ( MSG_ERROR , " KaY-%s: Out of memory " , __func__ ) ;
goto fail ;
2014-03-25 20:39:02 +01:00
}
2016-08-12 15:07:35 +02:00
sa_key - > key = key ;
sa_key - > key_len = key_len ;
os_memcpy ( sa_key - > key_identifier . mi , participant - > mi , MI_LEN ) ;
sa_key - > key_identifier . kn = kay - > dist_kn ;
sa_key - > confidentiality_offset = kay - > macsec_confidentiality ;
sa_key - > an = kay - > dist_an ;
ieee802_1x_kay_init_data_key ( sa_key ) ;
2014-03-25 20:39:02 +01:00
participant - > new_key = sa_key ;
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_use_data_key ( sa_key ) ;
2014-03-25 20:39:02 +01:00
dl_list_add ( & participant - > sak_list , & sa_key - > list ) ;
2016-10-21 14:45:29 +02:00
2016-08-12 15:07:35 +02:00
ieee802_1x_cp_set_ciphersuite ( kay - > cp , cs - > id ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2016-08-12 15:07:35 +02:00
ieee802_1x_cp_set_offset ( kay - > cp , kay - > macsec_confidentiality ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2016-08-12 15:07:35 +02:00
ieee802_1x_cp_set_distributedki ( kay - > cp , & sa_key - > key_identifier ) ;
ieee802_1x_cp_set_distributedan ( kay - > cp , sa_key - > an ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_signal_newsak ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list )
2020-04-24 00:27:57 +02:00
peer - > sak_used = false ;
2014-03-25 20:39:02 +01:00
2016-08-15 11:43:42 +02:00
kay - > dist_kn + + ;
kay - > dist_an + + ;
if ( kay - > dist_an > 3 )
kay - > dist_an = 0 ;
2014-03-25 20:39:02 +01:00
2016-08-15 11:43:42 +02:00
kay - > dist_time = time ( NULL ) ;
2014-03-25 20:39:02 +01:00
return 0 ;
2016-08-12 15:07:35 +02:00
fail :
os_free ( key ) ;
os_free ( context ) ;
return - 1 ;
2014-03-25 20:39:02 +01:00
}
2016-08-15 11:43:42 +02:00
static int compare_priorities ( const struct ieee802_1x_kay_peer * peer ,
const struct ieee802_1x_kay_peer * other )
{
if ( peer - > key_server_priority < other - > key_server_priority )
return - 1 ;
if ( other - > key_server_priority < peer - > key_server_priority )
return 1 ;
return os_memcmp ( peer - > sci . addr , other - > sci . addr , ETH_ALEN ) ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_elect_key_server - elect the key server
* when to elect : whenever the live peers list changes
*/
static int
ieee802_1x_kay_elect_key_server ( struct ieee802_1x_mka_participant * participant )
{
struct ieee802_1x_kay_peer * peer ;
struct ieee802_1x_kay_peer * key_server = NULL ;
struct ieee802_1x_kay * kay = participant - > kay ;
2020-04-24 00:27:57 +02:00
bool i_is_key_server ;
2018-02-20 20:28:37 +01:00
int priority_comparison ;
2014-03-25 20:39:02 +01:00
if ( participant - > is_obliged_key_server ) {
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
participant - > to_dist_sak = false ;
ieee802_1x_cp_set_electedself ( kay - > cp , true ) ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
/* elect the key server among the peers */
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( ! peer - > is_key_server )
continue ;
if ( ! key_server ) {
key_server = peer ;
continue ;
}
2016-08-15 11:43:42 +02:00
if ( compare_priorities ( peer , key_server ) < 0 )
2014-03-25 20:39:02 +01:00
key_server = peer ;
}
/* elect the key server between me and the above elected peer */
2020-04-24 00:27:57 +02:00
i_is_key_server = false ;
2014-03-25 20:39:02 +01:00
if ( key_server & & participant - > can_be_key_server ) {
2016-08-15 11:43:42 +02:00
struct ieee802_1x_kay_peer tmp ;
tmp . key_server_priority = kay - > actor_priority ;
os_memcpy ( & tmp . sci , & kay - > actor_sci , sizeof ( tmp . sci ) ) ;
2018-02-20 20:28:37 +01:00
priority_comparison = compare_priorities ( & tmp , key_server ) ;
if ( priority_comparison < 0 ) {
2020-04-24 00:27:57 +02:00
i_is_key_server = true ;
2018-02-20 20:28:37 +01:00
} else if ( priority_comparison = = 0 ) {
wpa_printf ( MSG_WARNING ,
" KaY: Cannot elect key server between me and peer, duplicate MAC detected " ) ;
key_server = NULL ;
}
2016-07-19 11:56:51 +02:00
} else if ( participant - > can_be_key_server ) {
2020-04-24 00:27:57 +02:00
i_is_key_server = true ;
2014-03-25 20:39:02 +01:00
}
if ( i_is_key_server ) {
2020-04-24 00:27:57 +02:00
ieee802_1x_cp_set_electedself ( kay - > cp , true ) ;
2016-08-15 11:43:41 +02:00
if ( ! sci_equal ( & kay - > key_server_sci , & kay - > actor_sci ) ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_signal_chgdserver ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
}
2020-04-24 00:27:57 +02:00
participant - > is_key_server = true ;
participant - > principal = true ;
participant - > new_sak = true ;
2018-06-15 17:06:41 +02:00
wpa_printf ( MSG_DEBUG , " KaY: I am elected as key server " ) ;
2020-04-24 00:27:57 +02:00
participant - > to_dist_sak = false ;
participant - > is_elected = true ;
2014-03-25 20:39:02 +01:00
os_memcpy ( & kay - > key_server_sci , & kay - > actor_sci ,
sizeof ( kay - > key_server_sci ) ) ;
kay - > key_server_priority = kay - > actor_priority ;
2016-07-19 11:56:51 +02:00
} else if ( key_server ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: Peer %s was elected as the key server " ,
mi_txt ( key_server - > mi ) ) ;
2020-04-24 00:27:57 +02:00
ieee802_1x_cp_set_electedself ( kay - > cp , false ) ;
2016-08-15 11:43:41 +02:00
if ( ! sci_equal ( & kay - > key_server_sci , & key_server - > sci ) ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_signal_chgdserver ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
}
2020-04-24 00:27:57 +02:00
participant - > is_key_server = false ;
participant - > principal = true ;
participant - > is_elected = true ;
2014-03-25 20:39:02 +01:00
os_memcpy ( & kay - > key_server_sci , & key_server - > sci ,
sizeof ( kay - > key_server_sci ) ) ;
kay - > key_server_priority = key_server - > key_server_priority ;
2016-07-19 11:56:51 +02:00
} else {
2020-04-24 00:27:57 +02:00
participant - > principal = false ;
participant - > is_key_server = false ;
participant - > is_elected = false ;
2014-03-25 20:39:02 +01:00
}
return 0 ;
}
/**
* ieee802_1x_kay_decide_macsec_use - the key server determinate
* how to use MACsec : whether use MACsec and its capability
* protectFrames will be advised if the key server and one of its live peers are
* MACsec capable and one of those request MACsec protection
*/
static int
ieee802_1x_kay_decide_macsec_use (
struct ieee802_1x_mka_participant * participant )
{
struct ieee802_1x_kay * kay = participant - > kay ;
struct ieee802_1x_kay_peer * peer ;
enum macsec_cap less_capability ;
2020-04-24 00:27:57 +02:00
bool has_peer ;
2014-03-25 20:39:02 +01:00
if ( ! participant - > is_key_server )
return - 1 ;
/* key server self is MACsec-desired and requesting MACsec */
if ( ! kay - > macsec_desired ) {
2020-04-24 00:27:57 +02:00
participant - > advised_desired = false ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
if ( kay - > macsec_capable = = MACSEC_CAP_NOT_IMPLEMENTED ) {
2020-04-24 00:27:57 +02:00
participant - > advised_desired = false ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
less_capability = kay - > macsec_capable ;
/* at least one of peers is MACsec-desired and requesting MACsec */
2020-04-24 00:27:57 +02:00
has_peer = false ;
2014-03-25 20:39:02 +01:00
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( ! peer - > macsec_desired )
continue ;
2016-08-15 11:43:41 +02:00
if ( peer - > macsec_capability = = MACSEC_CAP_NOT_IMPLEMENTED )
2014-03-25 20:39:02 +01:00
continue ;
2016-08-15 11:43:41 +02:00
less_capability = ( less_capability < peer - > macsec_capability ) ?
less_capability : peer - > macsec_capability ;
2020-04-24 00:27:57 +02:00
has_peer = true ;
2014-03-25 20:39:02 +01:00
}
if ( has_peer ) {
2020-04-24 00:27:57 +02:00
participant - > advised_desired = true ;
2014-03-25 20:39:02 +01:00
participant - > advised_capability = less_capability ;
2020-04-24 00:27:57 +02:00
kay - > authenticated = false ;
kay - > secured = true ;
kay - > failed = false ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_connect_secure ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
} else {
2020-04-24 00:27:57 +02:00
participant - > advised_desired = false ;
2014-03-25 20:39:02 +01:00
participant - > advised_capability = MACSEC_CAP_NOT_IMPLEMENTED ;
2020-04-24 00:27:57 +02:00
participant - > to_use_sak = false ;
kay - > authenticated = true ;
kay - > secured = false ;
kay - > failed = false ;
2014-03-25 20:39:02 +01:00
kay - > ltx_kn = 0 ;
kay - > ltx_an = 0 ;
kay - > lrx_kn = 0 ;
kay - > lrx_an = 0 ;
kay - > otx_kn = 0 ;
kay - > otx_an = 0 ;
kay - > orx_kn = 0 ;
kay - > orx_an = 0 ;
ieee802_1x_cp_connect_authenticated ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
}
return 0 ;
}
static const u8 pae_group_addr [ ETH_ALEN ] = {
0x01 , 0x80 , 0xc2 , 0x00 , 0x00 , 0x03
} ;
/**
* ieee802_1x_kay_encode_mkpdu -
*/
static int
ieee802_1x_kay_encode_mkpdu ( struct ieee802_1x_mka_participant * participant ,
struct wpabuf * pbuf )
{
unsigned int i ;
struct ieee8023_hdr * ether_hdr ;
struct ieee802_1x_hdr * eapol_hdr ;
ether_hdr = wpabuf_put ( pbuf , sizeof ( * ether_hdr ) ) ;
os_memcpy ( ether_hdr - > dest , pae_group_addr , sizeof ( ether_hdr - > dest ) ) ;
os_memcpy ( ether_hdr - > src , participant - > kay - > actor_sci . addr ,
sizeof ( ether_hdr - > dest ) ) ;
ether_hdr - > ethertype = host_to_be16 ( ETH_P_EAPOL ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Ethernet header: DA= " MACSTR " SA= " MACSTR
" Ethertype=0x%x " ,
MAC2STR ( ether_hdr - > dest ) , MAC2STR ( ether_hdr - > src ) ,
be_to_host16 ( ether_hdr - > ethertype ) ) ;
2014-03-25 20:39:02 +01:00
eapol_hdr = wpabuf_put ( pbuf , sizeof ( * eapol_hdr ) ) ;
eapol_hdr - > version = EAPOL_VERSION ;
eapol_hdr - > type = IEEE802_1X_TYPE_EAPOL_MKA ;
2018-12-27 15:19:46 +01:00
eapol_hdr - > length = host_to_be16 ( wpabuf_tailroom ( pbuf ) ) ;
wpa_printf ( MSG_DEBUG ,
" KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u " ,
eapol_hdr - > version , eapol_hdr - > type ,
be_to_host16 ( eapol_hdr - > length ) ) ;
2014-03-25 20:39:02 +01:00
2016-08-15 11:43:41 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( mka_body_handler ) ; i + + ) {
if ( mka_body_handler [ i ] . body_present & &
mka_body_handler [ i ] . body_present ( participant ) ) {
if ( mka_body_handler [ i ] . body_tx ( participant , pbuf ) )
2014-03-25 20:39:02 +01:00
return - 1 ;
}
}
return 0 ;
}
2018-12-27 15:19:46 +01:00
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_participant_send_mkpdu -
*/
static int
ieee802_1x_participant_send_mkpdu (
struct ieee802_1x_mka_participant * participant )
{
struct wpabuf * buf ;
struct ieee802_1x_kay * kay = participant - > kay ;
size_t length = 0 ;
unsigned int i ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Encode and send an MKPDU (ifname=%s) " ,
kay - > if_name ) ;
2014-03-25 20:39:02 +01:00
length + = sizeof ( struct ieee802_1x_hdr ) + sizeof ( struct ieee8023_hdr ) ;
2016-08-15 11:43:41 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( mka_body_handler ) ; i + + ) {
if ( mka_body_handler [ i ] . body_present & &
mka_body_handler [ i ] . body_present ( participant ) )
length + = mka_body_handler [ i ] . body_length ( participant ) ;
2014-03-25 20:39:02 +01:00
}
buf = wpabuf_alloc ( length ) ;
if ( ! buf ) {
wpa_printf ( MSG_ERROR , " KaY: out of memory " ) ;
return - 1 ;
}
if ( ieee802_1x_kay_encode_mkpdu ( participant , buf ) ) {
2018-06-15 17:06:41 +02:00
wpa_printf ( MSG_ERROR , " KaY: encode mkpdu fail " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2018-12-27 15:19:46 +01:00
wpa_hexdump_buf ( MSG_MSGDUMP , " KaY: Outgoing MKPDU " , buf ) ;
2014-03-25 20:39:02 +01:00
l2_packet_send ( kay - > l2_mka , NULL , 0 , wpabuf_head ( buf ) , wpabuf_len ( buf ) ) ;
wpabuf_free ( buf ) ;
2020-04-24 00:27:57 +02:00
kay - > active = true ;
participant - > active = true ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
static void ieee802_1x_kay_deinit_transmit_sa ( struct transmit_sa * psa ) ;
2016-10-21 14:45:28 +02:00
static void ieee802_1x_delete_transmit_sa ( struct ieee802_1x_kay * kay ,
struct transmit_sa * sa )
{
secy_disable_transmit_sa ( kay , sa ) ;
secy_delete_transmit_sa ( kay , sa ) ;
ieee802_1x_kay_deinit_transmit_sa ( sa ) ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_participant_timer -
*/
static void ieee802_1x_participant_timer ( void * eloop_ctx , void * timeout_ctx )
{
struct ieee802_1x_mka_participant * participant ;
struct ieee802_1x_kay * kay ;
struct ieee802_1x_kay_peer * peer , * pre_peer ;
time_t now = time ( NULL ) ;
2020-04-24 00:27:57 +02:00
bool lp_changed ;
2014-03-25 20:39:02 +01:00
struct receive_sc * rxsc , * pre_rxsc ;
struct transmit_sa * txsa , * pre_txsa ;
participant = ( struct ieee802_1x_mka_participant * ) eloop_ctx ;
kay = participant - > kay ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Participant timer (ifname=%s) " ,
kay - > if_name ) ;
2014-03-25 20:39:02 +01:00
if ( participant - > cak_life ) {
2016-08-15 11:43:42 +02:00
if ( now > participant - > cak_life )
goto delete_mka ;
2014-03-25 20:39:02 +01:00
}
/* should delete MKA instance if there are not live peers
* when the MKA life elapsed since its creating */
if ( participant - > mka_life ) {
if ( dl_list_empty ( & participant - > live_peers ) ) {
2016-08-15 11:43:42 +02:00
if ( now > participant - > mka_life )
goto delete_mka ;
2014-03-25 20:39:02 +01:00
} else {
participant - > mka_life = 0 ;
}
}
2020-04-24 00:27:57 +02:00
lp_changed = false ;
2014-03-25 20:39:02 +01:00
dl_list_for_each_safe ( peer , pre_peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( now > peer - > expire ) {
wpa_printf ( MSG_DEBUG , " KaY: Live peer removed " ) ;
wpa_hexdump ( MSG_DEBUG , " \t MI: " , peer - > mi ,
sizeof ( peer - > mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t MN: %d " , peer - > mn ) ;
dl_list_for_each_safe ( rxsc , pre_rxsc ,
& participant - > rxsc_list ,
struct receive_sc , list ) {
2016-08-15 11:43:41 +02:00
if ( sci_equal ( & rxsc - > sci , & peer - > sci ) ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_kay_deinit_receive_sc (
participant , rxsc ) ;
}
}
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
2020-04-24 00:27:57 +02:00
lp_changed = true ;
2014-03-25 20:39:02 +01:00
}
}
if ( lp_changed ) {
if ( dl_list_empty ( & participant - > live_peers ) ) {
2020-04-24 00:27:57 +02:00
participant - > advised_desired = false ;
2014-03-25 20:39:02 +01:00
participant - > advised_capability =
MACSEC_CAP_NOT_IMPLEMENTED ;
2020-04-24 00:27:57 +02:00
participant - > to_use_sak = false ;
participant - > ltx = false ;
participant - > lrx = false ;
participant - > otx = false ;
participant - > orx = false ;
participant - > is_key_server = false ;
participant - > is_elected = false ;
kay - > authenticated = false ;
kay - > secured = false ;
kay - > failed = false ;
2014-03-25 20:39:02 +01:00
kay - > ltx_kn = 0 ;
kay - > ltx_an = 0 ;
kay - > lrx_kn = 0 ;
kay - > lrx_an = 0 ;
kay - > otx_kn = 0 ;
kay - > otx_an = 0 ;
kay - > orx_kn = 0 ;
kay - > orx_an = 0 ;
dl_list_for_each_safe ( txsa , pre_txsa ,
& participant - > txsc - > sa_list ,
struct transmit_sa , list ) {
2016-10-21 14:45:28 +02:00
ieee802_1x_delete_transmit_sa ( kay , txsa ) ;
2014-03-25 20:39:02 +01:00
}
2018-02-20 20:28:34 +01:00
ieee802_1x_cp_connect_pending ( kay - > cp ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_sm_step ( kay - > cp ) ;
} else {
ieee802_1x_kay_elect_key_server ( participant ) ;
ieee802_1x_kay_decide_macsec_use ( participant ) ;
}
}
dl_list_for_each_safe ( peer , pre_peer , & participant - > potential_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( now > peer - > expire ) {
wpa_printf ( MSG_DEBUG , " KaY: Potential peer removed " ) ;
wpa_hexdump ( MSG_DEBUG , " \t MI: " , peer - > mi ,
sizeof ( peer - > mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t MN: %d " , peer - > mn ) ;
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
}
}
2018-12-29 00:27:22 +01:00
if ( participant - > new_sak & & participant - > is_key_server ) {
2014-03-25 20:39:02 +01:00
if ( ! ieee802_1x_kay_generate_new_sak ( participant ) )
2020-04-24 00:27:57 +02:00
participant - > to_dist_sak = true ;
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
participant - > new_sak = false ;
2014-03-25 20:39:02 +01:00
}
2017-02-07 09:58:31 +01:00
if ( participant - > retry_count < MAX_RETRY_CNT | |
participant - > mode = = PSK ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_participant_send_mkpdu ( participant ) ;
participant - > retry_count + + ;
}
2018-02-20 20:28:38 +01:00
eloop_register_timeout ( kay - > mka_hello_time / 1000 , 0 ,
2014-03-25 20:39:02 +01:00
ieee802_1x_participant_timer ,
participant , NULL ) ;
2016-08-15 11:43:42 +02:00
return ;
delete_mka :
2020-04-24 00:27:57 +02:00
kay - > authenticated = false ;
kay - > secured = false ;
kay - > failed = true ;
2016-08-15 11:43:42 +02:00
ieee802_1x_kay_delete_mka ( kay , & participant - > ckn ) ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_init_transmit_sa -
*/
static struct transmit_sa *
ieee802_1x_kay_init_transmit_sa ( struct transmit_sc * psc , u8 an , u32 next_PN ,
struct data_key * key )
{
struct transmit_sa * psa ;
2020-04-24 00:27:57 +02:00
key - > tx_latest = true ;
key - > rx_latest = true ;
2014-03-25 20:39:02 +01:00
psa = os_zalloc ( sizeof ( * psa ) ) ;
if ( ! psa ) {
wpa_printf ( MSG_ERROR , " %s: out of memory " , __func__ ) ;
return NULL ;
}
if ( key - > confidentiality_offset > = CONFIDENTIALITY_OFFSET_0 & &
key - > confidentiality_offset < = CONFIDENTIALITY_OFFSET_50 )
2020-04-24 00:27:57 +02:00
psa - > confidentiality = true ;
2014-03-25 20:39:02 +01:00
else
2020-04-24 00:27:57 +02:00
psa - > confidentiality = false ;
2014-03-25 20:39:02 +01:00
psa - > an = an ;
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_use_data_key ( key ) ;
2014-03-25 20:39:02 +01:00
psa - > pkey = key ;
psa - > next_pn = next_PN ;
psa - > sc = psc ;
os_get_time ( & psa - > created_time ) ;
2020-04-24 00:27:57 +02:00
psa - > in_use = false ;
2014-03-25 20:39:02 +01:00
dl_list_add ( & psc - > sa_list , & psa - > list ) ;
wpa_printf ( MSG_DEBUG ,
2018-06-15 17:06:41 +02:00
" KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC " ,
2016-10-21 14:45:26 +02:00
an , next_PN ) ;
2014-03-25 20:39:02 +01:00
return psa ;
}
/**
* ieee802_1x_kay_deinit_transmit_sa -
*/
static void ieee802_1x_kay_deinit_transmit_sa ( struct transmit_sa * psa )
{
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_deinit_data_key ( psa - > pkey ) ;
2014-03-25 20:39:02 +01:00
psa - > pkey = NULL ;
wpa_printf ( MSG_DEBUG ,
2016-08-12 15:07:33 +02:00
" KaY: Delete transmit SA(an: %hhu) of SC " ,
psa - > an ) ;
2014-03-25 20:39:02 +01:00
dl_list_del ( & psa - > list ) ;
os_free ( psa ) ;
}
/**
* init_transmit_sc -
*/
static struct transmit_sc *
2016-10-21 14:45:26 +02:00
ieee802_1x_kay_init_transmit_sc ( const struct ieee802_1x_mka_sci * sci )
2014-03-25 20:39:02 +01:00
{
struct transmit_sc * psc ;
psc = os_zalloc ( sizeof ( * psc ) ) ;
if ( ! psc ) {
wpa_printf ( MSG_ERROR , " %s: out of memory " , __func__ ) ;
return NULL ;
}
os_memcpy ( & psc - > sci , sci , sizeof ( psc - > sci ) ) ;
os_get_time ( & psc - > created_time ) ;
2020-04-24 00:27:57 +02:00
psc - > transmitting = false ;
psc - > encoding_sa = false ;
psc - > enciphering_sa = false ;
2014-03-25 20:39:02 +01:00
dl_list_init ( & psc - > sa_list ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Create transmit SC - SCI: %s " ,
sci_txt ( & psc - > sci ) ) ;
2014-03-25 20:39:02 +01:00
return psc ;
}
/**
* ieee802_1x_kay_deinit_transmit_sc -
*/
static void
ieee802_1x_kay_deinit_transmit_sc (
struct ieee802_1x_mka_participant * participant , struct transmit_sc * psc )
{
struct transmit_sa * psa , * tmp ;
2016-10-21 14:45:26 +02:00
wpa_printf ( MSG_DEBUG , " KaY: Delete transmit SC " ) ;
2016-10-21 14:45:28 +02:00
dl_list_for_each_safe ( psa , tmp , & psc - > sa_list , struct transmit_sa , list )
ieee802_1x_delete_transmit_sa ( participant - > kay , psa ) ;
2014-03-25 20:39:02 +01:00
2017-03-16 14:01:55 +01:00
secy_delete_transmit_sc ( participant - > kay , psc ) ;
2014-03-25 20:39:02 +01:00
os_free ( psc ) ;
}
/****************** Interface between CP and KAY *********************/
/**
* ieee802_1x_kay_set_latest_sa_attr -
*/
int ieee802_1x_kay_set_latest_sa_attr ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * lki , u8 lan ,
2020-04-24 00:27:57 +02:00
bool ltx , bool lrx )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * principal ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
if ( ! lki )
os_memset ( & principal - > lki , 0 , sizeof ( principal - > lki ) ) ;
else
os_memcpy ( & principal - > lki , lki , sizeof ( principal - > lki ) ) ;
principal - > lan = lan ;
principal - > ltx = ltx ;
principal - > lrx = lrx ;
if ( ! lki ) {
kay - > ltx_kn = 0 ;
kay - > lrx_kn = 0 ;
} else {
kay - > ltx_kn = lki - > kn ;
kay - > lrx_kn = lki - > kn ;
}
kay - > ltx_an = lan ;
kay - > lrx_an = lan ;
return 0 ;
}
/**
* ieee802_1x_kay_set_old_sa_attr -
*/
int ieee802_1x_kay_set_old_sa_attr ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * oki ,
2020-04-24 00:27:57 +02:00
u8 oan , bool otx , bool orx )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * principal ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
if ( ! oki )
os_memset ( & principal - > oki , 0 , sizeof ( principal - > oki ) ) ;
else
os_memcpy ( & principal - > oki , oki , sizeof ( principal - > oki ) ) ;
principal - > oan = oan ;
principal - > otx = otx ;
principal - > orx = orx ;
if ( ! oki ) {
kay - > otx_kn = 0 ;
kay - > orx_kn = 0 ;
} else {
kay - > otx_kn = oki - > kn ;
kay - > orx_kn = oki - > kn ;
}
kay - > otx_an = oan ;
kay - > orx_an = oan ;
return 0 ;
}
2016-10-21 14:45:28 +02:00
static struct transmit_sa * lookup_txsa_by_an ( struct transmit_sc * txsc , u8 an )
{
struct transmit_sa * txsa ;
dl_list_for_each ( txsa , & txsc - > sa_list , struct transmit_sa , list ) {
if ( txsa - > an = = an )
return txsa ;
}
return NULL ;
}
static struct receive_sa * lookup_rxsa_by_an ( struct receive_sc * rxsc , u8 an )
{
struct receive_sa * rxsa ;
dl_list_for_each ( rxsa , & rxsc - > sa_list , struct receive_sa , list ) {
if ( rxsa - > an = = an )
return rxsa ;
}
return NULL ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_create_sas -
*/
int ieee802_1x_kay_create_sas ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * lki )
{
struct data_key * sa_key , * latest_sak ;
struct ieee802_1x_mka_participant * principal ;
struct receive_sc * rxsc ;
struct receive_sa * rxsa ;
struct transmit_sa * txsa ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
latest_sak = NULL ;
dl_list_for_each ( sa_key , & principal - > sak_list , struct data_key , list ) {
if ( is_ki_equal ( & sa_key - > key_identifier , lki ) ) {
2020-04-24 00:27:57 +02:00
sa_key - > rx_latest = true ;
sa_key - > tx_latest = true ;
2014-03-25 20:39:02 +01:00
latest_sak = sa_key ;
2020-04-24 00:27:57 +02:00
principal - > to_use_sak = true ;
2014-03-25 20:39:02 +01:00
} else {
2020-04-24 00:27:57 +02:00
sa_key - > rx_latest = false ;
sa_key - > tx_latest = false ;
2014-03-25 20:39:02 +01:00
}
}
if ( ! latest_sak ) {
2018-06-15 17:06:41 +02:00
wpa_printf ( MSG_ERROR , " KaY: lki related sak not found " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
dl_list_for_each ( rxsc , & principal - > rxsc_list , struct receive_sc , list ) {
2016-10-21 14:45:28 +02:00
while ( ( rxsa = lookup_rxsa_by_an ( rxsc , latest_sak - > an ) ) ! = NULL )
ieee802_1x_delete_receive_sa ( kay , rxsa ) ;
2014-03-25 20:39:02 +01:00
rxsa = ieee802_1x_kay_init_receive_sa ( rxsc , latest_sak - > an , 1 ,
latest_sak ) ;
if ( ! rxsa )
return - 1 ;
secy_create_receive_sa ( kay , rxsa ) ;
}
2016-10-21 14:45:28 +02:00
while ( ( txsa = lookup_txsa_by_an ( principal - > txsc , latest_sak - > an ) ) ! =
NULL )
ieee802_1x_delete_transmit_sa ( kay , txsa ) ;
2014-03-25 20:39:02 +01:00
txsa = ieee802_1x_kay_init_transmit_sa ( principal - > txsc , latest_sak - > an ,
2018-11-02 19:02:17 +01:00
latest_sak - > next_pn ?
latest_sak - > next_pn : 1 ,
latest_sak ) ;
2014-03-25 20:39:02 +01:00
if ( ! txsa )
return - 1 ;
secy_create_transmit_sa ( kay , txsa ) ;
return 0 ;
}
/**
* ieee802_1x_kay_delete_sas -
*/
int ieee802_1x_kay_delete_sas ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * ki )
{
struct data_key * sa_key , * pre_key ;
struct transmit_sa * txsa , * pre_txsa ;
struct receive_sa * rxsa , * pre_rxsa ;
struct receive_sc * rxsc ;
struct ieee802_1x_mka_participant * principal ;
wpa_printf ( MSG_DEBUG , " KaY: Entry into %s " , __func__ ) ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
/* remove the transmit sa */
dl_list_for_each_safe ( txsa , pre_txsa , & principal - > txsc - > sa_list ,
struct transmit_sa , list ) {
2016-10-21 14:45:28 +02:00
if ( is_ki_equal ( & txsa - > pkey - > key_identifier , ki ) )
ieee802_1x_delete_transmit_sa ( kay , txsa ) ;
2014-03-25 20:39:02 +01:00
}
/* remove the receive sa */
dl_list_for_each ( rxsc , & principal - > rxsc_list , struct receive_sc , list ) {
dl_list_for_each_safe ( rxsa , pre_rxsa , & rxsc - > sa_list ,
struct receive_sa , list ) {
2016-10-21 14:45:28 +02:00
if ( is_ki_equal ( & rxsa - > pkey - > key_identifier , ki ) )
ieee802_1x_delete_receive_sa ( kay , rxsa ) ;
2014-03-25 20:39:02 +01:00
}
}
/* remove the sak */
dl_list_for_each_safe ( sa_key , pre_key , & principal - > sak_list ,
struct data_key , list ) {
if ( is_ki_equal ( & sa_key - > key_identifier , ki ) ) {
2018-12-27 23:47:53 +01:00
if ( principal - > new_key = = sa_key )
principal - > new_key = NULL ;
2016-10-21 14:45:29 +02:00
dl_list_del ( & sa_key - > list ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_kay_deinit_data_key ( sa_key ) ;
break ;
}
}
return 0 ;
}
/**
* ieee802_1x_kay_enable_tx_sas -
*/
int ieee802_1x_kay_enable_tx_sas ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * lki )
{
struct ieee802_1x_mka_participant * principal ;
struct transmit_sa * txsa ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
dl_list_for_each ( txsa , & principal - > txsc - > sa_list , struct transmit_sa ,
list ) {
if ( is_ki_equal ( & txsa - > pkey - > key_identifier , lki ) ) {
2020-04-24 00:27:57 +02:00
txsa - > in_use = true ;
2014-03-25 20:39:02 +01:00
secy_enable_transmit_sa ( kay , txsa ) ;
ieee802_1x_cp_set_usingtransmitas (
2020-04-24 00:27:57 +02:00
principal - > kay - > cp , true ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_sm_step ( principal - > kay - > cp ) ;
}
}
return 0 ;
}
/**
* ieee802_1x_kay_enable_rx_sas -
*/
int ieee802_1x_kay_enable_rx_sas ( struct ieee802_1x_kay * kay ,
struct ieee802_1x_mka_ki * lki )
{
struct ieee802_1x_mka_participant * principal ;
struct receive_sa * rxsa ;
struct receive_sc * rxsc ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
dl_list_for_each ( rxsc , & principal - > rxsc_list , struct receive_sc , list ) {
dl_list_for_each ( rxsa , & rxsc - > sa_list , struct receive_sa , list )
{
if ( is_ki_equal ( & rxsa - > pkey - > key_identifier , lki ) ) {
2020-04-24 00:27:57 +02:00
rxsa - > in_use = true ;
2014-03-25 20:39:02 +01:00
secy_enable_receive_sa ( kay , rxsa ) ;
ieee802_1x_cp_set_usingreceivesas (
2020-04-24 00:27:57 +02:00
principal - > kay - > cp , true ) ;
2014-03-25 20:39:02 +01:00
ieee802_1x_cp_sm_step ( principal - > kay - > cp ) ;
}
}
}
return 0 ;
}
/**
* ieee802_1x_kay_enable_new_info -
*/
int ieee802_1x_kay_enable_new_info ( struct ieee802_1x_kay * kay )
{
struct ieee802_1x_mka_participant * principal ;
principal = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! principal )
return - 1 ;
2017-02-07 09:58:31 +01:00
if ( principal - > retry_count < MAX_RETRY_CNT | | principal - > mode = = PSK ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_participant_send_mkpdu ( principal ) ;
principal - > retry_count + + ;
}
return 0 ;
}
/**
2021-09-19 08:24:27 +02:00
* ieee802_1x_kay_mkpdu_validity_check -
* Validity checks specified in IEEE Std 802.1 X - 2010 , 11.11 .2 ( Validation of
2018-12-27 15:19:46 +01:00
* MKPDUs )
2014-03-25 20:39:02 +01:00
*/
2021-09-19 08:24:27 +02:00
static int ieee802_1x_kay_mkpdu_validity_check ( struct ieee802_1x_kay * kay ,
const u8 * buf , size_t len )
2014-03-25 20:39:02 +01:00
{
struct ieee8023_hdr * eth_hdr ;
struct ieee802_1x_hdr * eapol_hdr ;
struct ieee802_1x_mka_hdr * mka_hdr ;
struct ieee802_1x_mka_basic_body * body ;
size_t mka_msg_len ;
struct ieee802_1x_mka_participant * participant ;
size_t body_len ;
2018-02-20 20:28:31 +01:00
size_t ckn_len ;
2014-03-25 20:39:02 +01:00
u8 icv [ MAX_ICV_LEN ] ;
2018-12-27 15:19:46 +01:00
const u8 * msg_icv ;
2014-03-25 20:39:02 +01:00
2018-12-27 15:19:46 +01:00
/* len > eth+eapol header already verified in kay_l2_receive();
* likewise , eapol_hdr - > length validated there */
2014-03-25 20:39:02 +01:00
eth_hdr = ( struct ieee8023_hdr * ) buf ;
eapol_hdr = ( struct ieee802_1x_hdr * ) ( eth_hdr + 1 ) ;
mka_hdr = ( struct ieee802_1x_mka_hdr * ) ( eapol_hdr + 1 ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Ethernet header: DA= " MACSTR " SA= " MACSTR
" Ethertype=0x%x " ,
MAC2STR ( eth_hdr - > dest ) , MAC2STR ( eth_hdr - > src ) ,
be_to_host16 ( eth_hdr - > ethertype ) ) ;
/* the destination address shall not be an individual address */
2014-03-25 20:39:02 +01:00
if ( os_memcmp ( eth_hdr - > dest , pae_group_addr , ETH_ALEN ) ! = 0 ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
2014-03-25 20:39:02 +01:00
" KaY: ethernet destination address is not PAE group address " ) ;
return - 1 ;
}
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u " ,
eapol_hdr - > version , eapol_hdr - > type ,
be_to_host16 ( eapol_hdr - > length ) ) ;
/* MKPDU shall not be less than 32 octets */
2014-03-25 20:39:02 +01:00
mka_msg_len = be_to_host16 ( eapol_hdr - > length ) ;
if ( mka_msg_len < 32 ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: MKPDU is less than 32 octets " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2018-12-27 15:19:46 +01:00
/* MKPDU shall be a multiple of 4 octets */
2014-03-25 20:39:02 +01:00
if ( ( mka_msg_len % 4 ) ! = 0 ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
2014-03-25 20:39:02 +01:00
" KaY: MKPDU is not multiple of 4 octets " ) ;
return - 1 ;
}
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_MSGDUMP , " KaY: EAPOL-MKA Packet Body (MKPDU) " ,
mka_hdr , mka_msg_len ) ;
/* Room for body_len already verified in kay_l2_receive() */
2014-03-25 20:39:02 +01:00
body = ( struct ieee802_1x_mka_basic_body * ) mka_hdr ;
body_len = get_mka_param_body_len ( body ) ;
/* EAPOL-MKA body should comprise basic parameter set and ICV */
if ( mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN ) {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV " ,
mka_msg_len , MKA_HDR_LEN ,
body_len , DEFAULT_ICV_LEN ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2018-02-20 20:28:31 +01:00
if ( body_len < sizeof ( struct ieee802_1x_mka_basic_body ) - MKA_HDR_LEN ) {
wpa_printf ( MSG_DEBUG , " KaY: Too small body length %zu " ,
body_len ) ;
return - 1 ;
}
ckn_len = body_len -
( sizeof ( struct ieee802_1x_mka_basic_body ) - MKA_HDR_LEN ) ;
if ( ckn_len < 1 | | ckn_len > MAX_CKN_LEN ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_WARNING ,
2018-02-20 20:28:31 +01:00
" KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes) " ,
ckn_len , MAX_CKN_LEN ) ;
return - 1 ;
}
2018-12-27 15:19:46 +01:00
ieee802_1x_mka_dump_basic_body ( body ) ;
2014-03-25 20:39:02 +01:00
/* CKN should be owned by I */
2018-02-20 20:28:31 +01:00
participant = ieee802_1x_kay_get_participant ( kay , body - > ckn , ckn_len ) ;
2014-03-25 20:39:02 +01:00
if ( ! participant ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: CKN is not included in my CA " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
/* algorithm agility check */
if ( os_memcmp ( body - > algo_agility , mka_algo_agility ,
sizeof ( body - > algo_agility ) ) ! = 0 ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_INFO ,
" KaY: Peer's algorithm agility (%s) not supported " ,
algo_agility_txt ( body - > algo_agility ) ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
/* ICV check */
/*
* The ICV will comprise the final octets of the packet body , whatever
* its size , not the fixed length 16 octets , indicated by the EAPOL
* packet body length .
*/
2018-12-27 15:19:46 +01:00
if ( len < mka_alg_tbl [ kay - > mka_algindex ] . icv_len | |
mka_alg_tbl [ kay - > mka_algindex ] . icv_hash (
2018-12-26 15:18:00 +01:00
participant - > ick . key , participant - > ick . len ,
2014-03-25 20:39:02 +01:00
buf , len - mka_alg_tbl [ kay - > mka_algindex ] . icv_len , icv ) ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_ERROR , " KaY: Failed to calculate ICV " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2016-08-15 11:43:42 +02:00
2018-12-27 15:19:46 +01:00
msg_icv = ieee802_1x_mka_decode_icv_body ( participant ,
( const u8 * ) mka_hdr ,
2014-03-25 20:39:02 +01:00
mka_msg_len ) ;
2016-08-15 11:43:42 +02:00
if ( ! msg_icv ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_WARNING , " KaY: No ICV in MKPDU - ignore it " ) ;
2014-03-25 20:39:02 +01:00
return - 1 ;
}
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_DEBUG , " KaY: Received ICV " ,
msg_icv , mka_alg_tbl [ kay - > mka_algindex ] . icv_len ) ;
2016-08-15 11:43:42 +02:00
if ( os_memcmp_const ( msg_icv , icv ,
mka_alg_tbl [ kay - > mka_algindex ] . icv_len ) ! = 0 ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_WARNING ,
2016-08-15 11:43:42 +02:00
" KaY: Computed ICV is not equal to Received ICV " ) ;
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_DEBUG , " KaY: Calculated ICV " ,
icv , mka_alg_tbl [ kay - > mka_algindex ] . icv_len ) ;
2016-08-15 11:43:42 +02:00
return - 1 ;
}
2014-03-25 20:39:02 +01:00
return 0 ;
}
/**
* ieee802_1x_kay_decode_mkpdu -
*/
static int ieee802_1x_kay_decode_mkpdu ( struct ieee802_1x_kay * kay ,
const u8 * buf , size_t len )
{
struct ieee802_1x_mka_participant * participant ;
struct ieee802_1x_mka_hdr * hdr ;
2018-02-20 20:28:43 +01:00
struct ieee802_1x_kay_peer * peer ;
2014-03-25 20:39:02 +01:00
size_t body_len ;
size_t left_len ;
2016-08-07 10:40:55 +02:00
u8 body_type ;
2014-03-25 20:39:02 +01:00
int i ;
const u8 * pos ;
2020-04-24 00:27:57 +02:00
bool handled [ 256 ] ;
bool bad_sak_use = false ; /* Error detected while processing SAK Use
* parameter set */
bool i_in_peerlist , is_in_live_peer , is_in_potential_peer ;
2014-03-25 20:39:02 +01:00
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Decode received MKPDU (ifname=%s) " ,
kay - > if_name ) ;
2021-09-19 08:24:27 +02:00
if ( ieee802_1x_kay_mkpdu_validity_check ( kay , buf , len ) )
2014-03-25 20:39:02 +01:00
return - 1 ;
/* handle basic parameter set */
pos = buf + sizeof ( struct ieee8023_hdr ) + sizeof ( struct ieee802_1x_hdr ) ;
left_len = len - sizeof ( struct ieee8023_hdr ) -
sizeof ( struct ieee802_1x_hdr ) ;
participant = ieee802_1x_mka_decode_basic_body ( kay , pos , left_len ) ;
if ( ! participant )
return - 1 ;
/* to skip basic parameter set */
hdr = ( struct ieee802_1x_mka_hdr * ) pos ;
2017-08-17 23:51:58 +02:00
body_len = MKA_ALIGN_LENGTH ( get_mka_param_body_len ( hdr ) ) ;
2018-12-27 15:19:46 +01:00
if ( left_len < body_len + MKA_HDR_LEN )
return - 1 ;
2014-03-25 20:39:02 +01:00
pos + = body_len + MKA_HDR_LEN ;
left_len - = body_len + MKA_HDR_LEN ;
/* check i am in the peer's peer list */
2018-12-27 15:19:46 +01:00
i_in_peerlist = ieee802_1x_mka_i_in_peerlist ( participant , pos ,
left_len ) ;
is_in_live_peer = ieee802_1x_kay_is_in_live_peer (
participant , participant - > current_peer_id . mi ) ;
wpa_printf ( MSG_DEBUG , " KaY: i_in_peerlist=%s is_in_live_peer=%s " ,
yes_no ( i_in_peerlist ) , yes_no ( is_in_live_peer ) ) ;
if ( i_in_peerlist & & ! is_in_live_peer ) {
2014-03-25 20:39:02 +01:00
/* accept the peer as live peer */
2018-12-27 15:19:46 +01:00
is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer (
participant , participant - > current_peer_id . mi ) ;
if ( is_in_potential_peer ) {
2016-07-19 11:56:55 +02:00
if ( ! ieee802_1x_kay_move_live_peer (
participant ,
participant - > current_peer_id . mi ,
be_to_host32 ( participant - >
current_peer_id . mn ) ) )
return - 1 ;
2016-08-15 11:43:42 +02:00
} else if ( ! ieee802_1x_kay_create_live_peer (
participant , participant - > current_peer_id . mi ,
be_to_host32 ( participant - >
current_peer_id . mn ) ) ) {
return - 1 ;
2014-03-25 20:39:02 +01:00
}
2016-08-15 11:43:42 +02:00
ieee802_1x_kay_elect_key_server ( participant ) ;
ieee802_1x_kay_decide_macsec_use ( participant ) ;
2014-03-25 20:39:02 +01:00
}
/*
* Handle other parameter set than basic parameter set .
* Each parameter set should be present only once .
*/
for ( i = 0 ; i < 256 ; i + + )
2020-04-24 00:27:57 +02:00
handled [ i ] = false ;
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
handled [ 0 ] = true ;
2016-08-12 15:07:33 +02:00
for ( ; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN ;
pos + = body_len + MKA_HDR_LEN ,
left_len - = body_len + MKA_HDR_LEN ) {
2014-03-25 20:39:02 +01:00
hdr = ( struct ieee802_1x_mka_hdr * ) pos ;
2017-08-17 23:51:58 +02:00
body_len = MKA_ALIGN_LENGTH ( get_mka_param_body_len ( hdr ) ) ;
2014-03-25 20:39:02 +01:00
body_type = get_mka_param_body_type ( hdr ) ;
if ( body_type = = MKA_ICV_INDICATOR )
return 0 ;
if ( left_len < ( MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN ) ) {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV " ,
left_len , MKA_HDR_LEN ,
body_len , DEFAULT_ICV_LEN ) ;
2017-08-18 01:14:28 +02:00
return - 1 ;
2014-03-25 20:39:02 +01:00
}
2018-12-27 15:19:46 +01:00
if ( handled [ body_type ] ) {
wpa_printf ( MSG_DEBUG ,
" KaY: Ignore duplicated body type %u " ,
body_type ) ;
2016-08-12 15:07:33 +02:00
continue ;
2018-12-27 15:19:46 +01:00
}
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
handled [ body_type ] = true ;
2016-08-15 11:43:41 +02:00
if ( body_type < ARRAY_SIZE ( mka_body_handler ) & &
mka_body_handler [ body_type ] . body_rx ) {
2018-02-20 20:28:43 +01:00
if ( mka_body_handler [ body_type ] . body_rx
( participant , pos , left_len ) ! = 0 ) {
/* Handle parameter set failure */
if ( body_type ! = MKA_SAK_USE ) {
wpa_printf ( MSG_INFO ,
" KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed " ,
body_type ) ;
return - 1 ;
}
/* Ideally DIST-SAK should be processed before
* SAK - USE . Unfortunately IEEE Std 802.1 X - 2010 ,
* 11.11 .3 ( Encoding MKPDUs ) states SAK - USE ( 3 )
* must always be encoded before DIST - SAK ( 4 ) .
* Rather than redesigning mka_body_handler so
* that it somehow processes DIST - SAK before
* SAK - USE , just ignore SAK - USE failures if
* DIST - SAK is also present in this MKPDU . */
2020-04-24 00:27:57 +02:00
bad_sak_use = true ;
2018-02-20 20:28:43 +01:00
}
2014-03-25 20:39:02 +01:00
} else {
wpa_printf ( MSG_ERROR ,
2018-12-27 15:19:46 +01:00
" KaY: The body type %d is not supported in this MKA version %d " ,
2014-03-25 20:39:02 +01:00
body_type , MKA_VERSION_ID ) ;
}
}
2018-02-20 20:28:43 +01:00
if ( bad_sak_use & & ! handled [ MKA_DISTRIBUTED_SAK ] ) {
wpa_printf ( MSG_INFO ,
" KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed " ,
MKA_SAK_USE ) ;
2019-01-08 04:49:54 +01:00
if ( ! reset_participant_mi ( participant ) )
wpa_printf ( MSG_DEBUG , " KaY: Could not update mi " ) ;
else
wpa_printf ( MSG_DEBUG ,
" KaY: Selected a new random MI: %s " ,
mi_txt ( participant - > mi ) ) ;
2018-02-20 20:28:43 +01:00
return - 1 ;
}
2018-02-20 20:28:44 +01:00
/* Detect missing parameter sets */
peer = ieee802_1x_kay_get_live_peer ( participant ,
participant - > current_peer_id . mi ) ;
if ( peer ) {
/* MKPDU is from live peer */
if ( ! handled [ MKA_SAK_USE ] ) {
/* Once a live peer starts sending SAK-USE, it should be
* sent every time . */
if ( peer - > sak_used ) {
wpa_printf ( MSG_INFO ,
" KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE " ) ;
return - 1 ;
}
/* Live peer is probably hung if it hasn't sent SAK-USE
* after a reasonable number of MKPDUs . Drop the MKPDU ,
* which will eventually force an timeout . */
if ( + + peer - > missing_sak_use_count >
MAX_MISSING_SAK_USE ) {
wpa_printf ( MSG_INFO ,
" KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE " ) ;
return - 1 ;
}
} else {
peer - > missing_sak_use_count = 0 ;
2018-02-20 20:28:45 +01:00
/* Only update live peer watchdog after successful
* decode of all parameter sets */
peer - > expire = time ( NULL ) + MKA_LIFE_TIME / 1000 ;
2018-02-20 20:28:44 +01:00
}
} else {
/* MKPDU is from new or potential peer */
peer = ieee802_1x_kay_get_peer ( participant ,
participant - > current_peer_id . mi ) ;
2018-12-27 15:19:46 +01:00
if ( ! peer ) {
wpa_printf ( MSG_DEBUG , " KaY: No peer entry found " ) ;
2018-02-20 20:28:45 +01:00
return - 1 ;
2018-12-27 15:19:46 +01:00
}
2018-02-20 20:28:44 +01:00
2018-02-20 20:28:45 +01:00
/* Do not update potential peer watchdog. Per IEEE Std
* 802.1 X - 2010 , 9.4 .3 , potential peers need to show liveness by
* including our MI / MN in their transmitted MKPDU ( within
* potential or live parameter sets ) . Whena potential peer does
* include our MI / MN in an MKPDU , we respond by moving the peer
* from ' potential_peers ' to ' live_peers ' . */
}
2018-02-20 20:28:43 +01:00
2020-04-24 00:27:57 +02:00
kay - > active = true ;
2014-03-25 20:39:02 +01:00
participant - > retry_count = 0 ;
2020-04-24 00:27:57 +02:00
participant - > active = true ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
static void kay_l2_receive ( void * ctx , const u8 * src_addr , const u8 * buf ,
size_t len )
{
struct ieee802_1x_kay * kay = ctx ;
struct ieee8023_hdr * eth_hdr ;
struct ieee802_1x_hdr * eapol_hdr ;
2018-12-27 15:19:46 +01:00
size_t calc_len ;
/* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
2014-03-25 20:39:02 +01:00
/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
if ( len < sizeof ( * eth_hdr ) + sizeof ( * eapol_hdr ) ) {
wpa_printf ( MSG_MSGDUMP , " KaY: EAPOL frame too short (%lu) " ,
( unsigned long ) len ) ;
return ;
}
eth_hdr = ( struct ieee8023_hdr * ) buf ;
eapol_hdr = ( struct ieee802_1x_hdr * ) ( eth_hdr + 1 ) ;
2018-12-27 15:19:46 +01:00
calc_len = sizeof ( * eth_hdr ) + sizeof ( * eapol_hdr ) +
be_to_host16 ( eapol_hdr - > length ) ;
if ( len < calc_len ) {
wpa_printf ( MSG_MSGDUMP , " KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u) " ,
2014-03-25 20:39:02 +01:00
( unsigned long ) len ,
2018-12-27 15:19:46 +01:00
( unsigned long ) calc_len ,
be_to_host16 ( eapol_hdr - > length ) ) ;
2014-03-25 20:39:02 +01:00
return ;
}
2018-12-27 15:19:46 +01:00
if ( len > calc_len ) {
wpa_hexdump ( MSG_DEBUG ,
" KaY: Ignore extra octets following the Packey Body field " ,
& buf [ calc_len ] , len - calc_len ) ;
len = calc_len ;
}
2014-03-25 20:39:02 +01:00
if ( eapol_hdr - > version < EAPOL_VERSION ) {
wpa_printf ( MSG_MSGDUMP , " KaY: version %d does not support MKA " ,
eapol_hdr - > version ) ;
return ;
}
2016-06-24 00:38:48 +02:00
if ( be_to_host16 ( eth_hdr - > ethertype ) ! = ETH_P_PAE | |
2014-03-25 20:39:02 +01:00
eapol_hdr - > type ! = IEEE802_1X_TYPE_EAPOL_MKA )
2018-12-27 15:19:46 +01:00
return ; /* ignore other EAPOL types silently here */
2014-03-25 20:39:02 +01:00
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_DEBUG , " KaY: RX EAPOL-MKA " , buf , len ) ;
2014-03-25 20:39:02 +01:00
if ( dl_list_empty ( & kay - > participant_list ) ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_ERROR ,
" KaY: No MKA participant instance - ignore EAPOL-MKA " ) ;
2014-03-25 20:39:02 +01:00
return ;
}
ieee802_1x_kay_decode_mkpdu ( kay , buf , len ) ;
}
/**
* ieee802_1x_kay_init -
*/
struct ieee802_1x_kay *
ieee802_1x_kay_init ( struct ieee802_1x_kay_ctx * ctx , enum macsec_policy policy ,
2020-04-24 00:27:57 +02:00
bool macsec_replay_protect , u32 macsec_replay_window ,
2016-12-05 15:53:55 +01:00
u16 port , u8 priority , const char * ifname , const u8 * addr )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_kay * kay ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Initialize - ifname=%s addr= " MACSTR
" port=%u priority=%u " ,
ifname , MAC2STR ( addr ) , port , priority ) ;
2014-03-25 20:39:02 +01:00
kay = os_zalloc ( sizeof ( * kay ) ) ;
if ( ! kay ) {
wpa_printf ( MSG_ERROR , " KaY-%s: out of memory " , __func__ ) ;
2017-08-22 10:34:19 +02:00
os_free ( ctx ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
kay - > ctx = ctx ;
2020-04-24 00:27:57 +02:00
kay - > enable = true ;
kay - > active = false ;
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
kay - > authenticated = false ;
kay - > secured = false ;
kay - > failed = false ;
2014-03-25 20:39:02 +01:00
kay - > policy = policy ;
os_strlcpy ( kay - > if_name , ifname , IFNAMSIZ ) ;
os_memcpy ( kay - > actor_sci . addr , addr , ETH_ALEN ) ;
2016-11-02 16:38:39 +01:00
kay - > actor_sci . port = host_to_be16 ( port ? port : 0x0001 ) ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Generated SCI: %s " ,
sci_txt ( & kay - > actor_sci ) ) ;
2016-12-05 15:53:55 +01:00
kay - > actor_priority = priority ;
2014-03-25 20:39:02 +01:00
/* While actor acts as a key server, shall distribute sakey */
kay - > dist_kn = 1 ;
kay - > dist_an = 0 ;
kay - > dist_time = 0 ;
kay - > pn_exhaustion = PENDING_PN_EXHAUSTION ;
kay - > macsec_csindex = DEFAULT_CS_INDEX ;
kay - > mka_algindex = DEFAULT_MKA_ALG_INDEX ;
kay - > mka_version = MKA_VERSION_ID ;
os_memcpy ( kay - > algo_agility , mka_algo_agility ,
sizeof ( kay - > algo_agility ) ) ;
dl_list_init ( & kay - > participant_list ) ;
2016-11-15 18:06:23 +01:00
if ( policy ! = DO_NOT_SECURE & &
2017-08-22 10:34:19 +02:00
secy_get_capability ( kay , & kay - > macsec_capable ) < 0 )
goto error ;
2016-11-15 18:06:23 +01:00
if ( policy = = DO_NOT_SECURE | |
kay - > macsec_capable = = MACSEC_CAP_NOT_IMPLEMENTED ) {
2014-03-25 20:39:02 +01:00
kay - > macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED ;
2020-04-24 00:27:57 +02:00
kay - > macsec_desired = false ;
kay - > macsec_protect = false ;
kay - > macsec_encrypt = false ;
2014-10-30 10:43:47 +01:00
kay - > macsec_validate = Disabled ;
2020-04-24 00:27:57 +02:00
kay - > macsec_replay_protect = false ;
2014-03-25 20:39:02 +01:00
kay - > macsec_replay_window = 0 ;
kay - > macsec_confidentiality = CONFIDENTIALITY_NONE ;
2018-02-20 20:28:38 +01:00
kay - > mka_hello_time = MKA_HELLO_TIME ;
2014-03-25 20:39:02 +01:00
} else {
2020-04-24 00:27:57 +02:00
kay - > macsec_desired = true ;
kay - > macsec_protect = true ;
2018-03-02 21:10:51 +01:00
if ( kay - > macsec_capable > = MACSEC_CAP_INTEG_AND_CONF & &
policy = = SHOULD_ENCRYPT ) {
2020-04-24 00:27:57 +02:00
kay - > macsec_encrypt = true ;
2018-03-02 21:10:51 +01:00
kay - > macsec_confidentiality = CONFIDENTIALITY_OFFSET_0 ;
} else { /* SHOULD_SECURE */
2020-04-24 00:27:57 +02:00
kay - > macsec_encrypt = false ;
2018-03-02 21:10:51 +01:00
kay - > macsec_confidentiality = CONFIDENTIALITY_NONE ;
}
2014-10-30 10:43:47 +01:00
kay - > macsec_validate = Strict ;
2018-11-02 19:02:14 +01:00
kay - > macsec_replay_protect = macsec_replay_protect ;
kay - > macsec_replay_window = macsec_replay_window ;
2018-02-20 20:28:38 +01:00
kay - > mka_hello_time = MKA_HELLO_TIME ;
2014-03-25 20:39:02 +01:00
}
wpa_printf ( MSG_DEBUG , " KaY: state machine created " ) ;
/* Initialize the SecY must be prio to CP, as CP will control SecY */
2017-08-22 10:34:19 +02:00
if ( secy_init_macsec ( kay ) < 0 ) {
wpa_printf ( MSG_DEBUG , " KaY: Could not initialize MACsec " ) ;
goto error ;
}
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " KaY: secy init macsec done " ) ;
/* init CP */
2016-08-12 15:07:35 +02:00
kay - > cp = ieee802_1x_cp_sm_init ( kay ) ;
2017-08-22 10:34:19 +02:00
if ( kay - > cp = = NULL )
goto error ;
2014-03-25 20:39:02 +01:00
if ( policy = = DO_NOT_SECURE ) {
ieee802_1x_cp_connect_authenticated ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
} else {
kay - > l2_mka = l2_packet_init ( kay - > if_name , NULL , ETH_P_PAE ,
kay_l2_receive , kay , 1 ) ;
if ( kay - > l2_mka = = NULL ) {
wpa_printf ( MSG_WARNING ,
" KaY: Failed to initialize L2 packet processing for MKA packet " ) ;
2017-08-22 10:34:19 +02:00
goto error ;
2014-03-25 20:39:02 +01:00
}
}
return kay ;
2017-08-22 10:34:19 +02:00
error :
ieee802_1x_kay_deinit ( kay ) ;
return NULL ;
2014-03-25 20:39:02 +01:00
}
/**
* ieee802_1x_kay_deinit -
*/
void
ieee802_1x_kay_deinit ( struct ieee802_1x_kay * kay )
{
struct ieee802_1x_mka_participant * participant ;
if ( ! kay )
return ;
wpa_printf ( MSG_DEBUG , " KaY: state machine removed " ) ;
while ( ! dl_list_empty ( & kay - > participant_list ) ) {
participant = dl_list_entry ( kay - > participant_list . next ,
struct ieee802_1x_mka_participant ,
list ) ;
ieee802_1x_kay_delete_mka ( kay , & participant - > ckn ) ;
}
ieee802_1x_cp_sm_deinit ( kay - > cp ) ;
secy_deinit_macsec ( kay ) ;
if ( kay - > l2_mka ) {
l2_packet_deinit ( kay - > l2_mka ) ;
kay - > l2_mka = NULL ;
}
os_free ( kay - > ctx ) ;
os_free ( kay ) ;
}
2018-12-27 15:19:46 +01:00
static const char * mode_txt ( enum mka_created_mode mode )
{
switch ( mode ) {
case PSK :
return " PSK " ;
case EAP_EXCHANGE :
return " EAP " ;
}
return " ? " ;
}
2014-03-25 20:39:02 +01:00
/**
* ieee802_1x_kay_create_mka -
*/
struct ieee802_1x_mka_participant *
2018-03-11 16:04:34 +01:00
ieee802_1x_kay_create_mka ( struct ieee802_1x_kay * kay ,
const struct mka_key_name * ckn ,
const struct mka_key * cak , u32 life ,
2020-04-24 00:27:57 +02:00
enum mka_created_mode mode , bool is_authenticator )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * participant ;
unsigned int usecs ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG ,
" KaY: Create MKA (ifname=%s mode=%s authenticator=%s) " ,
kay - > if_name , mode_txt ( mode ) , yes_no ( is_authenticator ) ) ;
2014-03-25 20:39:02 +01:00
if ( ! kay | | ! ckn | | ! cak ) {
wpa_printf ( MSG_ERROR , " KaY: ckn or cak is null " ) ;
return NULL ;
}
2018-12-26 15:28:27 +01:00
if ( cak - > len ! = 16 & & cak - > len ! = 32 ) {
wpa_printf ( MSG_ERROR , " KaY: Unexpected CAK length %u " ,
( unsigned int ) cak - > len ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
if ( ckn - > len > MAX_CKN_LEN ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_ERROR , " KaY: CKN is out of range (>32 bytes) " ) ;
2014-03-25 20:39:02 +01:00
return NULL ;
}
if ( ! kay - > enable ) {
wpa_printf ( MSG_ERROR , " KaY: Now is at disable state " ) ;
return NULL ;
}
participant = os_zalloc ( sizeof ( * participant ) ) ;
if ( ! participant ) {
wpa_printf ( MSG_ERROR , " KaY-%s: out of memory " , __func__ ) ;
return NULL ;
}
participant - > ckn . len = ckn - > len ;
os_memcpy ( participant - > ckn . name , ckn - > name , ckn - > len ) ;
2018-12-27 15:19:46 +01:00
wpa_hexdump ( MSG_DEBUG , " KaY: CKN " , participant - > ckn . name ,
participant - > ckn . len ) ;
2014-03-25 20:39:02 +01:00
participant - > cak . len = cak - > len ;
os_memcpy ( participant - > cak . key , cak - > key , cak - > len ) ;
2018-12-27 15:19:46 +01:00
wpa_hexdump_key ( MSG_DEBUG , " KaY: CAK " , participant - > cak . key ,
participant - > cak . len ) ;
2014-03-25 20:39:02 +01:00
if ( life )
participant - > cak_life = life + time ( NULL ) ;
switch ( mode ) {
case EAP_EXCHANGE :
if ( is_authenticator ) {
2020-04-24 00:27:57 +02:00
participant - > is_obliged_key_server = true ;
participant - > can_be_key_server = true ;
participant - > is_key_server = true ;
participant - > principal = true ;
2014-03-25 20:39:02 +01:00
os_memcpy ( & kay - > key_server_sci , & kay - > actor_sci ,
sizeof ( kay - > key_server_sci ) ) ;
kay - > key_server_priority = kay - > actor_priority ;
2020-04-24 00:27:57 +02:00
participant - > is_elected = true ;
2014-03-25 20:39:02 +01:00
} else {
2020-04-24 00:27:57 +02:00
participant - > is_obliged_key_server = false ;
participant - > can_be_key_server = false ;
participant - > is_key_server = false ;
participant - > is_elected = true ;
2014-03-25 20:39:02 +01:00
}
break ;
default :
2020-04-24 00:27:57 +02:00
participant - > is_obliged_key_server = false ;
participant - > can_be_key_server = true ;
participant - > is_key_server = true ;
participant - > is_elected = false ;
2014-03-25 20:39:02 +01:00
break ;
}
2020-04-24 00:27:57 +02:00
participant - > cached = false ;
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
participant - > active = false ;
participant - > participant = false ;
participant - > retain = false ;
2014-03-25 20:39:02 +01:00
participant - > activate = DEFAULT ;
if ( participant - > is_key_server )
2020-04-24 00:27:57 +02:00
participant - > principal = true ;
2014-03-25 20:39:02 +01:00
dl_list_init ( & participant - > live_peers ) ;
dl_list_init ( & participant - > potential_peers ) ;
participant - > retry_count = 0 ;
participant - > kay = kay ;
2016-08-12 15:07:33 +02:00
if ( ! reset_participant_mi ( participant ) )
2014-10-11 17:46:35 +02:00
goto fail ;
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_DEBUG , " KaY: Selected random MI: %s " ,
mi_txt ( participant - > mi ) ) ;
2014-03-25 20:39:02 +01:00
2020-04-24 00:27:57 +02:00
participant - > lrx = false ;
participant - > ltx = false ;
participant - > orx = false ;
participant - > otx = false ;
participant - > to_dist_sak = false ;
participant - > to_use_sak = false ;
participant - > new_sak = false ;
2014-03-25 20:39:02 +01:00
dl_list_init ( & participant - > sak_list ) ;
participant - > new_key = NULL ;
dl_list_init ( & participant - > rxsc_list ) ;
2016-10-21 14:45:26 +02:00
participant - > txsc = ieee802_1x_kay_init_transmit_sc ( & kay - > actor_sci ) ;
2014-12-09 15:20:31 +01:00
secy_cp_control_protect_frames ( kay , kay - > macsec_protect ) ;
secy_cp_control_replay ( kay , kay - > macsec_replay_protect ,
kay - > macsec_replay_window ) ;
2018-11-02 19:02:16 +01:00
if ( secy_create_transmit_sc ( kay , participant - > txsc ) )
goto fail ;
2014-03-25 20:39:02 +01:00
/* to derive KEK from CAK and CKN */
2018-12-26 15:23:29 +01:00
participant - > kek . len = participant - > cak . len ;
2014-03-25 20:39:02 +01:00
if ( mka_alg_tbl [ kay - > mka_algindex ] . kek_trfm ( participant - > cak . key ,
2018-12-26 11:35:18 +01:00
participant - > cak . len ,
2014-03-25 20:39:02 +01:00
participant - > ckn . name ,
participant - > ckn . len ,
2018-12-26 11:35:18 +01:00
participant - > kek . key ,
participant - > kek . len ) ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_ERROR , " KaY: KEK derivation failed " ) ;
2014-03-25 20:39:02 +01:00
goto fail ;
}
wpa_hexdump_key ( MSG_DEBUG , " KaY: Derived KEK " ,
participant - > kek . key , participant - > kek . len ) ;
/* to derive ICK from CAK and CKN */
2018-12-26 15:23:29 +01:00
participant - > ick . len = participant - > cak . len ;
2014-03-25 20:39:02 +01:00
if ( mka_alg_tbl [ kay - > mka_algindex ] . ick_trfm ( participant - > cak . key ,
2018-12-26 11:39:21 +01:00
participant - > cak . len ,
2014-03-25 20:39:02 +01:00
participant - > ckn . name ,
participant - > ckn . len ,
2018-12-26 11:39:21 +01:00
participant - > ick . key ,
participant - > ick . len ) ) {
2018-12-27 15:19:46 +01:00
wpa_printf ( MSG_ERROR , " KaY: ICK derivation failed " ) ;
2014-03-25 20:39:02 +01:00
goto fail ;
}
wpa_hexdump_key ( MSG_DEBUG , " KaY: Derived ICK " ,
participant - > ick . key , participant - > ick . len ) ;
dl_list_add ( & kay - > participant_list , & participant - > list ) ;
2018-02-20 20:28:38 +01:00
usecs = os_random ( ) % ( kay - > mka_hello_time * 1000 ) ;
2014-03-25 20:39:02 +01:00
eloop_register_timeout ( 0 , usecs , ieee802_1x_participant_timer ,
participant , NULL ) ;
2016-11-02 16:38:36 +01:00
/* Disable MKA lifetime for PSK mode.
* The peer ( s ) can take a long time to come up , because we
* create a " standby " MKA , and we need it to remain live until
* some peer appears .
*/
if ( mode ! = PSK ) {
participant - > mka_life = MKA_LIFE_TIME / 1000 + time ( NULL ) +
usecs / 1000000 ;
}
2017-02-07 09:58:31 +01:00
participant - > mode = mode ;
2014-03-25 20:39:02 +01:00
return participant ;
fail :
2018-11-02 19:02:16 +01:00
os_free ( participant - > txsc ) ;
2014-03-25 20:39:02 +01:00
os_free ( participant ) ;
return NULL ;
}
/**
* ieee802_1x_kay_delete_mka -
*/
void
ieee802_1x_kay_delete_mka ( struct ieee802_1x_kay * kay , struct mka_key_name * ckn )
{
struct ieee802_1x_mka_participant * participant ;
struct ieee802_1x_kay_peer * peer ;
struct data_key * sak ;
struct receive_sc * rxsc ;
if ( ! kay | | ! ckn )
return ;
wpa_printf ( MSG_DEBUG , " KaY: participant removed " ) ;
/* get the participant */
2018-02-20 20:28:31 +01:00
participant = ieee802_1x_kay_get_participant ( kay , ckn - > name , ckn - > len ) ;
2014-03-25 20:39:02 +01:00
if ( ! participant ) {
wpa_hexdump ( MSG_DEBUG , " KaY: participant is not found " ,
ckn - > name , ckn - > len ) ;
return ;
}
2016-07-19 11:56:58 +02:00
eloop_cancel_timeout ( ieee802_1x_participant_timer , participant , NULL ) ;
2014-03-25 20:39:02 +01:00
dl_list_del ( & participant - > list ) ;
/* remove live peer */
while ( ! dl_list_empty ( & participant - > live_peers ) ) {
peer = dl_list_entry ( participant - > live_peers . next ,
struct ieee802_1x_kay_peer , list ) ;
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
}
/* remove potential peer */
while ( ! dl_list_empty ( & participant - > potential_peers ) ) {
peer = dl_list_entry ( participant - > potential_peers . next ,
struct ieee802_1x_kay_peer , list ) ;
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
}
/* remove sak */
while ( ! dl_list_empty ( & participant - > sak_list ) ) {
sak = dl_list_entry ( participant - > sak_list . next ,
struct data_key , list ) ;
dl_list_del ( & sak - > list ) ;
2016-10-21 14:45:29 +02:00
ieee802_1x_kay_deinit_data_key ( sak ) ;
2014-03-25 20:39:02 +01:00
}
while ( ! dl_list_empty ( & participant - > rxsc_list ) ) {
rxsc = dl_list_entry ( participant - > rxsc_list . next ,
struct receive_sc , list ) ;
ieee802_1x_kay_deinit_receive_sc ( participant , rxsc ) ;
}
ieee802_1x_kay_deinit_transmit_sc ( participant , participant - > txsc ) ;
os_memset ( & participant - > cak , 0 , sizeof ( participant - > cak ) ) ;
os_memset ( & participant - > kek , 0 , sizeof ( participant - > kek ) ) ;
os_memset ( & participant - > ick , 0 , sizeof ( participant - > ick ) ) ;
os_free ( participant ) ;
}
/**
* ieee802_1x_kay_mka_participate -
*/
void ieee802_1x_kay_mka_participate ( struct ieee802_1x_kay * kay ,
2020-04-24 00:27:57 +02:00
struct mka_key_name * ckn , bool status )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * participant ;
if ( ! kay | | ! ckn )
return ;
2018-02-20 20:28:31 +01:00
participant = ieee802_1x_kay_get_participant ( kay , ckn - > name , ckn - > len ) ;
2014-03-25 20:39:02 +01:00
if ( ! participant )
return ;
participant - > active = status ;
}
/**
* ieee802_1x_kay_new_sak -
*/
int
ieee802_1x_kay_new_sak ( struct ieee802_1x_kay * kay )
{
struct ieee802_1x_mka_participant * participant ;
if ( ! kay )
return - 1 ;
participant = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( ! participant )
return - 1 ;
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " KaY: new SAK signal " ) ;
return 0 ;
}
/**
* ieee802_1x_kay_change_cipher_suite -
*/
int
2016-08-22 20:02:40 +02:00
ieee802_1x_kay_change_cipher_suite ( struct ieee802_1x_kay * kay ,
unsigned int cs_index )
2014-03-25 20:39:02 +01:00
{
struct ieee802_1x_mka_participant * participant ;
2016-10-07 12:08:12 +02:00
enum macsec_cap secy_cap ;
2014-03-25 20:39:02 +01:00
if ( ! kay )
return - 1 ;
2016-08-22 20:02:40 +02:00
if ( cs_index > = CS_TABLE_SIZE ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR ,
" KaY: Configured cipher suite index is out of range " ) ;
return - 1 ;
}
if ( kay - > macsec_csindex = = cs_index )
return - 2 ;
if ( cs_index = = 0 )
2020-04-24 00:27:57 +02:00
kay - > macsec_desired = false ;
2014-03-25 20:39:02 +01:00
kay - > macsec_csindex = cs_index ;
kay - > macsec_capable = cipher_suite_tbl [ kay - > macsec_csindex ] . capable ;
2016-10-07 12:08:12 +02:00
if ( secy_get_capability ( kay , & secy_cap ) < 0 )
return - 3 ;
if ( kay - > macsec_capable > secy_cap )
kay - > macsec_capable = secy_cap ;
2014-03-25 20:39:02 +01:00
participant = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( participant ) {
wpa_printf ( MSG_INFO , " KaY: Cipher Suite changed " ) ;
2020-04-24 00:27:57 +02:00
participant - > new_sak = true ;
2014-03-25 20:39:02 +01:00
}
return 0 ;
}
2016-12-15 21:10:53 +01:00
# ifdef CONFIG_CTRL_IFACE
2018-12-29 15:52:31 +01:00
2016-12-15 21:10:53 +01:00
/**
* ieee802_1x_kay_get_status - Get IEEE 802.1 X KaY status details
* @ sm : Pointer to KaY allocated with ieee802_1x_kay_init ( )
* @ buf : Buffer for status information
* @ buflen : Maximum buffer length
* @ verbose : Whether to include verbose status information
* Returns : Number of bytes written to buf .
*
2018-12-29 09:38:54 +01:00
* Query KaY status information . This function fills in a text area with current
2016-12-15 21:10:53 +01:00
* status information . If the buffer ( buf ) is not large enough , status
* information will be truncated to fit the buffer .
*/
int ieee802_1x_kay_get_status ( struct ieee802_1x_kay * kay , char * buf ,
size_t buflen )
{
2018-12-29 09:38:54 +01:00
char * pos , * end ;
int res , count ;
struct ieee802_1x_mka_participant * p ;
2016-12-15 21:10:53 +01:00
if ( ! kay )
return 0 ;
2018-12-29 09:38:54 +01:00
pos = buf ;
end = buf + buflen ;
res = os_snprintf ( pos , end - pos ,
2016-12-15 21:10:53 +01:00
" PAE KaY status=%s \n "
" Authenticated=%s \n "
" Secured=%s \n "
" Failed=%s \n "
" Actor Priority=%u \n "
" Key Server Priority=%u \n "
" Is Key Server=%s \n "
" Number of Keys Distributed=%u \n "
2018-02-20 20:28:38 +01:00
" Number of Keys Received=%u \n "
" MKA Hello Time=%u \n " ,
2016-12-15 21:10:53 +01:00
kay - > active ? " Active " : " Not-Active " ,
kay - > authenticated ? " Yes " : " No " ,
kay - > secured ? " Yes " : " No " ,
kay - > failed ? " Yes " : " No " ,
kay - > actor_priority ,
kay - > key_server_priority ,
kay - > is_key_server ? " Yes " : " No " ,
kay - > dist_kn - 1 ,
2018-02-20 20:28:38 +01:00
kay - > rcvd_keys ,
kay - > mka_hello_time ) ;
2018-12-29 09:38:54 +01:00
if ( os_snprintf_error ( buflen , res ) )
2016-12-15 21:10:53 +01:00
return 0 ;
2018-12-29 09:38:54 +01:00
pos + = res ;
res = os_snprintf ( pos , end - pos ,
" actor_sci=%s \n " , sci_txt ( & kay - > actor_sci ) ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos + = res ;
res = os_snprintf ( pos , end - pos ,
" key_server_sci=%s \n " , sci_txt ( & kay - > key_server_sci ) ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos + = res ;
count = 0 ;
dl_list_for_each ( p , & kay - > participant_list ,
struct ieee802_1x_mka_participant , list ) {
char * pos2 = pos ;
res = os_snprintf ( pos2 , end - pos2 , " participant_idx=%d \n ckn= " ,
count ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos2 + = res ;
count + + ;
pos2 + = wpa_snprintf_hex ( pos2 , end - pos2 , p - > ckn . name ,
p - > ckn . len ) ;
res = os_snprintf ( pos2 , end - pos2 ,
" \n mi=%s \n "
" mn=%u \n "
" active=%s \n "
" participant=%s \n "
" retain=%s \n "
" live_peers=%u \n "
" potential_peers=%u \n "
" is_key_server=%s \n "
" is_elected=%s \n " ,
mi_txt ( p - > mi ) , p - > mn ,
yes_no ( p - > active ) ,
yes_no ( p - > participant ) ,
yes_no ( p - > retain ) ,
dl_list_len ( & p - > live_peers ) ,
dl_list_len ( & p - > potential_peers ) ,
yes_no ( p - > is_key_server ) ,
yes_no ( p - > is_elected ) ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos2 + = res ;
pos = pos2 ;
}
return pos - buf ;
2016-12-15 21:10:53 +01:00
}
2018-12-29 15:52:31 +01:00
2020-04-24 00:27:57 +02:00
static const char * true_false ( bool val )
2018-12-29 15:52:31 +01:00
{
return val ? " true " : " false " ;
}
static const char * activate_control_txt ( enum activate_ctrl activate )
{
switch ( activate ) {
case DEFAULT :
return " default " ;
case DISABLED :
return " disabled " ;
case ON_OPER_UP :
return " onOperUp " ;
case ALWAYS :
return " always " ;
}
return " ? " ;
}
2020-04-24 00:27:57 +02:00
static char * mka_mib_peer ( struct dl_list * peers , bool live , char * buf ,
2018-12-29 15:52:31 +01:00
char * end )
{
char * pos = buf ;
struct ieee802_1x_kay_peer * p ;
int res ;
dl_list_for_each ( p , peers , struct ieee802_1x_kay_peer , list ) {
res = os_snprintf ( pos , end - pos ,
" ieee8021XKayMkaPeerListMI=%s \n "
" ieee8021XKayMkaPeerListMN=%u \n "
" ieee8021XKayMkaPeerListType=%u \n "
" ieee8021XKayMkaPeerListSCI=%s \n " ,
mi_txt ( p - > mi ) ,
p - > mn ,
live ? 1 : 2 ,
sci_txt ( & p - > sci ) ) ;
if ( os_snprintf_error ( end - pos , res ) )
return pos ;
pos + = res ;
}
return pos ;
}
int ieee802_1x_kay_get_mib ( struct ieee802_1x_kay * kay , char * buf ,
size_t buflen )
{
char * pos , * end ;
int res ;
struct ieee802_1x_mka_participant * p ;
if ( ! kay )
return 0 ;
pos = buf ;
end = buf + buflen ;
dl_list_for_each ( p , & kay - > participant_list ,
struct ieee802_1x_mka_participant , list ) {
char * pos2 = pos ;
res = os_snprintf ( pos2 , end - pos2 , " ieee8021XKayMkaPartCKN= " ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos2 + = res ;
pos2 + = wpa_snprintf_hex ( pos2 , end - pos2 , p - > ckn . name ,
p - > ckn . len ) ;
res = os_snprintf ( pos2 , end - pos2 ,
" \n ieee8021XKayMkaPartCached=%s \n "
" ieee8021XKayMkaPartActive=%s \n "
" ieee8021XKayMkaPartRetain=%s \n "
" ieee8021XKayMkaPartActivateControl=%s \n "
" ieee8021XKayMkaPartPrincipal=%s \n " ,
true_false ( p - > cached ) ,
true_false ( p - > active ) ,
true_false ( p - > retain ) ,
activate_control_txt ( p - > activate ) ,
true_false ( p - > principal ) ) ;
if ( os_snprintf_error ( buflen , res ) )
return end - pos ;
pos2 + = res ;
pos = pos2 ;
2020-04-24 00:27:57 +02:00
pos = mka_mib_peer ( & p - > live_peers , true , pos , end ) ;
pos = mka_mib_peer ( & p - > potential_peers , false , pos , end ) ;
2018-12-29 15:52:31 +01:00
}
return pos - buf ;
}
2016-12-15 21:10:53 +01:00
# endif /* CONFIG_CTRL_IFACE */