2014-03-25 20:39:02 +01:00
/*
* IEEE 802.1 X - 2010 Key Agree Protocol of PAE state machine
* 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 */
# 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 ,
. index = 0 ,
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 ,
2014-03-25 20:39:02 +01:00
/* 128-bit CAK, KEK, ICK, ICV */
2016-08-12 15:07:33 +02:00
. cak_len = DEFAULT_ICV_LEN ,
. kek_len = DEFAULT_ICV_LEN ,
. ick_len = DEFAULT_ICV_LEN ,
. icv_len = DEFAULT_ICV_LEN ,
. cak_trfm = ieee802_1x_cak_128bits_aes_cmac ,
. ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac ,
. kek_trfm = ieee802_1x_kek_128bits_aes_cmac ,
. ick_trfm = ieee802_1x_ick_128bits_aes_cmac ,
. icv_hash = ieee802_1x_icv_128bits_aes_cmac ,
2014-03-25 20:39:02 +01:00
2016-08-12 15:07:33 +02:00
. index = 1 ,
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 ;
}
/**
* 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 ;
body_len = get_mka_param_body_len ( body ) ;
wpa_printf ( MSG_DEBUG , " *** MKA Basic Parameter set *** " ) ;
wpa_printf ( MSG_DEBUG , " \t Version.......: %d " , body - > version ) ;
wpa_printf ( MSG_DEBUG , " \t Priority......: %d " , body - > priority ) ;
wpa_printf ( MSG_DEBUG , " \t KeySvr........: %d " , body - > key_server ) ;
wpa_printf ( MSG_DEBUG , " \t MACSecDesired.: %d " , body - > macsec_desired ) ;
2016-08-15 11:43:41 +02:00
wpa_printf ( MSG_DEBUG , " \t MACSecCapable.: %d " , body - > macsec_capability ) ;
2016-08-12 15:07:33 +02:00
wpa_printf ( MSG_DEBUG , " \t Body Length...: %zu " , body_len ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_DEBUG , " \t SCI MAC.......: " MACSTR ,
MAC2STR ( body - > actor_sci . addr ) ) ;
wpa_printf ( MSG_DEBUG , " \t SCI Port .....: %d " ,
be_to_host16 ( body - > actor_sci . port ) ) ;
wpa_hexdump ( MSG_DEBUG , " \t Member Id.....: " ,
body - > actor_mi , sizeof ( body - > actor_mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t Message Number: %d " ,
be_to_host32 ( body - > actor_mn ) ) ;
wpa_hexdump ( MSG_DEBUG , " \t Algo Agility..: " ,
body - > algo_agility , sizeof ( body - > algo_agility ) ) ;
wpa_hexdump_ascii ( MSG_DEBUG , " \t CAK Name......: " , body - > ckn ,
body_len + MKA_HDR_LEN - sizeof ( * body ) ) ;
}
/**
* 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 ;
body_len = get_mka_param_body_len ( body ) ;
if ( body - > type = = MKA_LIVE_PEER_LIST ) {
wpa_printf ( MSG_DEBUG , " *** Live Peer List *** " ) ;
2016-08-12 15:07:33 +02:00
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 ) {
wpa_printf ( MSG_DEBUG , " *** Potential Live Peer List *** " ) ;
2016-08-12 15:07:33 +02:00
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 ) ) ;
wpa_hexdump_ascii ( MSG_DEBUG , " \t Member Id.....: " , mi , MI_LEN ) ;
wpa_printf ( MSG_DEBUG , " \t Message Number: %d " , be_to_host32 ( mn ) ) ;
}
}
/**
* 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 ;
body_len = get_mka_param_body_len ( body ) ;
wpa_printf ( MSG_INFO , " *** Distributed SAK *** " ) ;
wpa_printf ( MSG_INFO , " \t Distributed AN........: %d " , body - > dan ) ;
wpa_printf ( MSG_INFO , " \t Confidentiality Offset: %d " ,
body - > confid_offset ) ;
2016-08-12 15:07:33 +02:00
wpa_printf ( MSG_INFO , " \t Body Length...........: %zu " , body_len ) ;
2014-03-25 20:39:02 +01:00
if ( ! body_len )
return ;
wpa_printf ( MSG_INFO , " \t Key Number............: %d " ,
be_to_host32 ( body - > kn ) ) ;
wpa_hexdump ( MSG_INFO , " \t AES Key Wrap of SAK...: " , body - > sak , 24 ) ;
}
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 ;
body_len = get_mka_param_body_len ( body ) ;
wpa_printf ( MSG_DEBUG , " *** MACsec SAK Use *** " ) ;
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 ) ) ;
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 ) ) ;
wpa_printf ( MSG_DEBUG , " \t Plain Key Tx....: %s " , yes_no ( body - > ptx ) ) ;
wpa_printf ( MSG_DEBUG , " \t Plain Key Rx....: %s " , yes_no ( body - > prx ) ) ;
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 ;
wpa_hexdump ( MSG_DEBUG , " \t Key Server MI....: " ,
body - > lsrv_mi , sizeof ( body - > lsrv_mi ) ) ;
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 ) ) ;
wpa_hexdump_ascii ( MSG_DEBUG , " \t Old Key Server MI....: " ,
body - > osrv_mi , sizeof ( body - > osrv_mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t Old Key Number.......: %u " ,
be_to_host32 ( body - > okn ) ) ;
wpa_printf ( MSG_DEBUG , " \t Old Lowest PN........: %u " ,
be_to_host32 ( body - > olpn ) ) ;
}
/**
* ieee802_1x_kay_get_participant -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_participant ( struct ieee802_1x_kay * kay , const u8 * ckn )
{
struct ieee802_1x_mka_participant * participant ;
dl_list_for_each ( participant , & kay - > participant_list ,
struct ieee802_1x_mka_participant , list ) {
if ( os_memcmp ( participant - > ckn . name , ckn ,
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
*/
static Boolean
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
*/
static Boolean
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 ,
2016-08-12 15:07:35 +02:00
const u8 * cs_id )
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 + + ) {
2016-08-12 15:07:35 +02:00
if ( cipher_suite_tbl [ i ] . id = = cs )
2016-08-15 11:43:41 +02:00
return & cipher_suite_tbl [ i ] ;
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-08-15 11:43:41 +02:00
static Boolean sci_equal ( const struct ieee802_1x_mka_sci * a ,
const struct ieee802_1x_mka_sci * b )
{
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 ;
}
/**
* 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 ;
}
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 ) ;
psa - > in_use = FALSE ;
dl_list_add ( & psc - > sa_list , & psa - > list ) ;
wpa_printf ( MSG_DEBUG ,
2016-08-12 15:07:33 +02:00
" KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d) " ,
an , lowest_pn , psc - > channel ) ;
2014-03-25 20:39:02 +01:00
return psa ;
}
/**
* ieee802_1x_kay_deinit_receive_sa -
*/
static void ieee802_1x_kay_deinit_receive_sa ( struct receive_sa * psa )
{
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 *
ieee802_1x_kay_init_receive_sc ( const struct ieee802_1x_mka_sci * psci ,
int channel )
{
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 ) ) ;
psc - > channel = channel ;
os_get_time ( & psc - > created_time ) ;
psc - > receiving = FALSE ;
dl_list_init ( & psc - > sa_list ) ;
wpa_printf ( MSG_DEBUG , " KaY: Create receive SC(channel: %d) " , channel ) ;
wpa_hexdump ( MSG_DEBUG , " SCI: " , ( u8 * ) psci , sizeof ( * psci ) ) ;
return psc ;
}
/**
* 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 ;
wpa_printf ( MSG_DEBUG , " KaY: Delete receive SC(channel: %d) " ,
psc - > channel ) ;
dl_list_for_each_safe ( psa , pre_sa , & psc - > sa_list , struct receive_sa ,
list ) {
secy_disable_receive_sa ( participant - > kay , psa ) ;
ieee802_1x_kay_deinit_receive_sa ( psa ) ;
}
dl_list_del ( & psc - > list ) ;
os_free ( psc ) ;
}
2016-08-15 11:43:41 +02:00
static void ieee802_1x_kay_dump_peer ( struct ieee802_1x_kay_peer * peer )
{
wpa_hexdump ( MSG_DEBUG , " \t MI: " , peer - > mi , sizeof ( peer - > mi ) ) ;
wpa_printf ( MSG_DEBUG , " \t MN: %d " , peer - > mn ) ;
wpa_hexdump ( MSG_DEBUG , " \t SCI Addr: " , peer - > sci . addr , ETH_ALEN ) ;
wpa_printf ( MSG_DEBUG , " \t Port: %d " , peer - > sci . port ) ;
}
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 ;
peer - > sak_used = FALSE ;
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 ;
u32 sc_ch = 0 ;
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 ) ) ;
secy_get_available_receive_sc ( participant - > kay , & sc_ch ) ;
rxsc = ieee802_1x_kay_init_receive_sc ( & peer - > sci , sc_ch ) ;
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
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 ) ;
secy_create_receive_sc ( participant - > kay , rxsc ) ;
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 ) ;
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 ;
u32 sc_ch = 0 ;
2016-08-15 11:43:41 +02:00
peer = ieee802_1x_kay_get_potential_peer ( participant , mi ) ;
2014-03-25 20:39:02 +01:00
2016-07-19 11:56:54 +02:00
rxsc = ieee802_1x_kay_init_receive_sc ( & participant - > current_peer_sci ,
sc_ch ) ;
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 ;
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 ) ;
dl_list_add_tail ( & participant - > live_peers , & peer - > list ) ;
secy_get_available_receive_sc ( participant - > kay , & sc_ch ) ;
dl_list_add ( & participant - > rxsc_list , & rxsc - > list ) ;
secy_create_receive_sc ( participant - > kay , rxsc ) ;
return peer ;
}
/**
* ieee802_1x_mka_basic_body_present -
*/
static Boolean
ieee802_1x_mka_basic_body_present (
struct ieee802_1x_mka_participant * participant )
{
return TRUE ;
}
/**
* 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 ;
unsigned int length = ieee802_1x_mka_basic_body_length ( participant ) ;
body = wpabuf_put ( buf , length ) ;
body - > version = kay - > mka_version ;
body - > priority = kay - > actor_priority ;
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 ;
}
2016-08-12 15:07:33 +02:00
static Boolean
reset_participant_mi ( struct ieee802_1x_mka_participant * participant )
{
if ( os_get_random ( participant - > mi , sizeof ( participant - > mi ) ) < 0 )
return FALSE ;
participant - > mn = 0 ;
return TRUE ;
}
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 ;
body = ( const struct ieee802_1x_mka_basic_body * ) mka_msg ;
if ( body - > version > MKA_VERSION_ID ) {
wpa_printf ( MSG_DEBUG ,
" KaY: peer's version(%d) greater than mka current version(%d) " ,
body - > version , MKA_VERSION_ID ) ;
}
if ( kay - > is_obliged_key_server & & body - > key_server ) {
wpa_printf ( MSG_DEBUG , " I must be as key server " ) ;
return NULL ;
}
participant = ieee802_1x_kay_get_participant ( kay , body - > ckn ) ;
if ( ! participant ) {
wpa_printf ( MSG_DEBUG , " Peer is not included in my CA " ) ;
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 ;
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 ) {
/* Check duplicated SCI */
/* TODO: What policy should be applied to detect duplicated SCI
* is active attacker or a valid peer whose MI is be changed ?
*/
peer = ieee802_1x_kay_get_peer_sci ( participant ,
& body - > actor_sci ) ;
if ( peer ) {
wpa_printf ( MSG_WARNING ,
" KaY: duplicated SCI detected, Maybe active attacker " ) ;
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
}
peer = ieee802_1x_kay_create_potential_peer (
participant , body - > actor_mi ,
be_to_host32 ( body - > actor_mn ) ) ;
if ( ! peer )
return NULL ;
peer - > macsec_desired = body - > macsec_desired ;
2016-08-15 11:43:41 +02:00
peer - > macsec_capability = body - > macsec_capability ;
2014-03-25 20:39:02 +01:00
peer - > is_key_server = ( Boolean ) body - > key_server ;
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 - > expire = time ( NULL ) + MKA_LIFE_TIME / 1000 ;
peer - > macsec_desired = body - > macsec_desired ;
2016-08-15 11:43:41 +02:00
peer - > macsec_capability = body - > macsec_capability ;
2014-03-25 20:39:02 +01:00
peer - > is_key_server = ( Boolean ) body - > key_server ;
peer - > key_server_priority = body - > priority ;
} else {
wpa_printf ( MSG_WARNING , " KaY: The peer MN have received " ) ;
return NULL ;
}
return participant ;
}
/**
* ieee802_1x_mka_live_peer_body_present
*/
static Boolean
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
*/
static Boolean
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 -
*/
static Boolean
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 ;
left_len - = body_len + MKA_HDR_LEN ,
pos + = 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 ) ;
if ( body_type ! = MKA_LIVE_PEER_LIST & &
body_type ! = MKA_POTENTIAL_PEER_LIST )
2016-08-12 15:07:33 +02:00
continue ;
2014-03-25 20:39:02 +01:00
ieee802_1x_mka_dump_peer_body (
( struct ieee802_1x_mka_peer_body * ) pos ) ;
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 ) ;
2016-08-12 15:07:33 +02:00
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
}
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 ,
MI_LEN ) = = 0 & &
be_to_host32 ( peer_mi - > mn ) = = participant - > mn )
return TRUE ;
}
2014-03-25 20:39:02 +01:00
}
return FALSE ;
}
/**
* 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 ;
Boolean is_included ;
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 ;
peer - > expire = time ( NULL ) + MKA_LIFE_TIME / 1000 ;
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
*/
static Boolean
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
}
/**
*
*/
static u32
ieee802_1x_mka_get_lpn ( struct ieee802_1x_mka_participant * principal ,
struct ieee802_1x_mka_ki * ki )
{
struct receive_sa * rxsa ;
struct receive_sc * rxsc ;
u32 lpn = 0 ;
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 , ki ) ) {
secy_get_receive_lowest_pn ( principal - > kay ,
rxsa ) ;
lpn = lpn > rxsa - > lowest_pn ?
lpn : rxsa - > lowest_pn ;
break ;
}
}
}
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 ;
u32 pn = 1 ;
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 ) {
body - > ptx = TRUE ;
body - > prx = TRUE ;
body - > lan = 0 ;
body - > lrx = FALSE ;
body - > ltx = FALSE ;
body - > delay_protect = FALSE ;
return 0 ;
}
/* data protect, lowest accept packet number */
2016-08-15 11:43:42 +02:00
body - > delay_protect = kay - > macsec_replay_protect ;
2014-03-25 20:39:02 +01:00
pn = ieee802_1x_mka_get_lpn ( participant , & participant - > lki ) ;
2016-08-15 11:43:42 +02:00
if ( pn > kay - > pn_exhaustion ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING , " KaY: My LPN exhaustion " ) ;
if ( participant - > is_key_server )
participant - > new_sak = TRUE ;
}
body - > llpn = host_to_be32 ( pn ) ;
pn = ieee802_1x_mka_get_lpn ( participant , & participant - > oki ) ;
body - > olpn = host_to_be32 ( pn ) ;
/* 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 ) {
body - > otx = TRUE ;
body - > orx = TRUE ;
os_memcpy ( body - > osrv_mi , participant - > oki . mi ,
sizeof ( body - > osrv_mi ) ) ;
body - > okn = host_to_be32 ( participant - > oki . kn ) ;
} else {
body - > otx = FALSE ;
body - > orx = FALSE ;
}
/* set CP's variable */
if ( body - > ltx ) {
2016-08-15 11:43:42 +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 )
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 transmit_sa * txsa ;
struct data_key * sa_key = NULL ;
size_t body_len ;
struct ieee802_1x_mka_ki ki ;
u32 lpn ;
Boolean all_receiving ;
2016-08-15 11:43:42 +02:00
Boolean found ;
2016-08-15 11:43:42 +02:00
struct ieee802_1x_kay * kay = participant - > kay ;
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 ) {
wpa_printf ( MSG_WARNING , " KaY: the peer is not my live peer " ) ;
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 " ) ;
/* check latest key is valid */
if ( body - > ltx | | body - > lrx ) {
2016-08-15 11:43:42 +02:00
found = FALSE ;
2014-03-25 20:39:02 +01:00
os_memcpy ( ki . mi , body - > lsrv_mi , sizeof ( ki . mi ) ) ;
2016-06-24 00:38:48 +02:00
ki . kn = be_to_host32 ( body - > lkn ) ;
2014-03-25 20:39:02 +01:00
dl_list_for_each ( sa_key , & participant - > sak_list ,
struct data_key , list ) {
if ( is_ki_equal ( & sa_key - > key_identifier , & ki ) ) {
2016-08-15 11:43:42 +02:00
found = TRUE ;
2014-03-25 20:39:02 +01:00
break ;
}
}
2016-08-15 11:43:42 +02:00
if ( ! found ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING , " KaY: Latest key is invalid " ) ;
return - 1 ;
}
if ( os_memcmp ( participant - > lki . mi , body - > lsrv_mi ,
sizeof ( participant - > lki . mi ) ) = = 0 & &
2016-06-24 00:38:48 +02:00
be_to_host32 ( body - > lkn ) = = participant - > lki . kn & &
2014-03-25 20:39:02 +01:00
body - > lan = = participant - > lan ) {
peer - > sak_used = TRUE ;
}
if ( body - > ltx & & peer - > is_key_server ) {
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_set_servertransmitting ( kay - > cp , TRUE ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2014-03-25 20:39:02 +01:00
}
}
/* check old key is valid */
if ( body - > otx | | body - > orx ) {
if ( os_memcmp ( participant - > oki . mi , body - > osrv_mi ,
sizeof ( participant - > oki . mi ) ) ! = 0 | |
2016-06-24 00:38:48 +02:00
be_to_host32 ( body - > okn ) ! = participant - > oki . kn | |
2014-03-25 20:39:02 +01:00
body - > oan ! = participant - > oan ) {
wpa_printf ( MSG_WARNING , " KaY: Old key is invalid " ) ;
return - 1 ;
}
}
/* 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 ,
" KaY: Lowest packet number should greater than 0 when delay_protect is TRUE " ) ;
return - 1 ;
}
/* check all live peer have used the sak for receiving sa */
all_receiving = TRUE ;
dl_list_for_each ( peer , & participant - > live_peers ,
struct ieee802_1x_kay_peer , list ) {
if ( ! peer - > sak_used ) {
all_receiving = FALSE ;
break ;
}
}
if ( all_receiving ) {
participant - > to_dist_sak = FALSE ;
2016-08-15 11:43:42 +02:00
ieee802_1x_cp_set_allreceiving ( kay - > cp , TRUE ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
2014-03-25 20:39:02 +01:00
}
/* if i'm key server, and detects peer member pn exhaustion, rekey.*/
2016-06-24 00:38:48 +02:00
lpn = be_to_host32 ( body - > llpn ) ;
2016-08-15 11:43:42 +02:00
if ( lpn > kay - > pn_exhaustion ) {
2014-03-25 20:39:02 +01:00
if ( participant - > is_key_server ) {
participant - > new_sak = TRUE ;
wpa_printf ( MSG_WARNING , " KaY: Peer LPN exhaustion " ) ;
}
}
2016-08-15 11:43:42 +02:00
found = FALSE ;
2014-03-25 20:39:02 +01:00
dl_list_for_each ( txsa , & participant - > txsc - > sa_list ,
struct transmit_sa , list ) {
if ( sa_key ! = NULL & & txsa - > pkey = = sa_key ) {
2016-08-15 11:43:42 +02:00
found = TRUE ;
2014-03-25 20:39:02 +01:00
break ;
}
}
2016-08-15 11:43:42 +02:00
if ( ! found ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING , " KaY: Can't find txsa " ) ;
return - 1 ;
}
/* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
* npn is larger than txsa ' s npn , set it to txsa .
*/
2016-08-15 11:43:42 +02:00
secy_get_transmit_next_pn ( kay , txsa ) ;
2014-03-25 20:39:02 +01:00
if ( lpn > txsa - > next_pn ) {
2016-08-15 11:43:42 +02:00
secy_set_transmit_next_pn ( kay , txsa ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_INFO , " KaY: update lpn =0x%x " , lpn ) ;
}
return 0 ;
}
/**
* ieee802_1x_mka_dist_sak_body_present
*/
static Boolean
ieee802_1x_mka_dist_sak_body_present (
struct ieee802_1x_mka_participant * participant )
{
2016-08-15 11:43:42 +02:00
return 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 ;
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 ;
}
2014-10-07 12:48:45 +02:00
if ( aes_wrap ( participant - > kek . key , 16 ,
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
{
2016-08-12 15:07:35 +02:00
pkey - > transmits = TRUE ;
pkey - > receives = TRUE ;
2014-03-25 20:39:02 +01:00
os_get_time ( & pkey - > created_time ) ;
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 ,
" KaY: I can't accept the distributed SAK as myself is key server " ) ;
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 ) {
2016-08-15 11:43:42 +02:00
kay - > authenticated = TRUE ;
kay - > secured = FALSE ;
kay - > failed = FALSE ;
2014-03-25 20:39:02 +01:00
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 ) ;
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_WARNING , " KaY:The Key server advise no MACsec " ) ;
participant - > to_use_sak = TRUE ;
return 0 ;
}
2016-08-15 11:43:42 +02:00
2014-03-25 20:39:02 +01:00
participant - > advised_desired = TRUE ;
2016-08-15 11:43:42 +02:00
kay - > authenticated = FALSE ;
kay - > secured = TRUE ;
kay - > failed = FALSE ;
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 ) ) {
wpa_printf ( MSG_WARNING , " KaY:The Key has installed " ) ;
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 {
cs = ieee802_1x_kay_get_cipher_suite ( participant , body - > sak ) ;
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 ;
2016-08-15 11:43:42 +02:00
kay - > macsec_csindex = cs - > index ;
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 ;
}
2014-10-07 12:48:45 +02:00
if ( aes_unwrap ( participant - > kek . key , 16 , sak_len > > 3 , wrap_sak ,
2014-03-25 20:39:02 +01:00
unwrap_sak ) ) {
wpa_printf ( MSG_ERROR , " KaY: AES unwrap failed " ) ;
os_free ( unwrap_sak ) ;
return - 1 ;
}
wpa_hexdump ( MSG_DEBUG , " \t AES Key Unwrap of SAK: " , unwrap_sak , sak_len ) ;
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
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
participant - > to_use_sak = TRUE ;
return 0 ;
}
/**
* ieee802_1x_mka_icv_body_present
*/
static Boolean
ieee802_1x_mka_icv_body_present ( struct ieee802_1x_mka_participant * participant )
{
return TRUE ;
}
/**
* ieee802_1x_kay_get_icv_length
*/
static int
ieee802_1x_mka_get_icv_length ( struct ieee802_1x_mka_participant * participant )
{
int length ;
length = sizeof ( struct ieee802_1x_mka_icv_body ) ;
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 ) ;
if ( length ! = DEFAULT_ICV_LEN ) {
body = wpabuf_put ( buf , MKA_HDR_LEN ) ;
body - > type = MKA_ICV_INDICATOR ;
set_mka_param_body_len ( body , length - MKA_HDR_LEN ) ;
}
if ( mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_hash (
participant - > ick . key , wpabuf_head ( buf ) , buf - > used , cmac ) ) {
wpa_printf ( MSG_ERROR , " KaY, omac1_aes_128 failed " ) ;
return - 1 ;
}
2016-08-12 15:07:33 +02:00
if ( length ! = DEFAULT_ICV_LEN )
length - = MKA_HDR_LEN ;
os_memcpy ( wpabuf_put ( buf , length ) , cmac , length ) ;
2014-03-25 20:39:02 +01:00
return 0 ;
}
/**
* ieee802_1x_mka_decode_icv_body -
*/
static u8 *
ieee802_1x_mka_decode_icv_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_icv_body * body ;
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 ;
while ( left_len > ( MKA_HDR_LEN + DEFAULT_ICV_LEN ) ) {
hdr = ( struct ieee802_1x_mka_hdr * ) pos ;
body_len = get_mka_param_body_len ( hdr ) ;
body_type = get_mka_param_body_type ( hdr ) ;
if ( left_len < ( body_len + MKA_HDR_LEN ) )
break ;
if ( body_type ! = MKA_ICV_INDICATOR ) {
left_len - = MKA_HDR_LEN + body_len ;
pos + = MKA_HDR_LEN + body_len ;
continue ;
}
body = ( struct ieee802_1x_mka_icv_body * ) pos ;
if ( body_len
< mka_alg_tbl [ participant - > kay - > mka_algindex ] . icv_len ) {
return NULL ;
}
return body - > icv ;
}
return ( u8 * ) ( mka_msg + msg_len - DEFAULT_ICV_LEN ) ;
}
/**
* 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 ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets " ,
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 ,
2016-08-12 15:07:33 +02:00
" KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets " ,
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 ) ;
Boolean ( * body_present ) ( struct ieee802_1x_mka_participant * participant ) ;
} ;
2016-08-15 11:43:41 +02:00
static struct mka_param_body_handler mka_body_handler [ ] = {
2014-03-25 20:39:02 +01:00
/* basic parameter set */
{
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
} ,
/* live peer list parameter set */
{
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
} ,
/* potential peer list parameter set */
{
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
} ,
/* sak use parameter set */
{
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
} ,
/* distribute sak parameter set */
{
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
} ,
/* distribute cak parameter set */
{
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
} ,
/* kmd parameter set */
{
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
} ,
/* announce parameter set */
{
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
} ,
/* icv parameter set */
{
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
} ,
} ;
/**
* ieee802_1x_kay_deinit_data_key -
*/
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 ;
dl_list_del ( & pkey - > list ) ;
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 ,
" KaY: Live peers list must not empty when generating fresh SAK " ) ;
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 ,
" KaY: Life time have not elapsed since prior SAK distributed " ) ;
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 ) ) ;
2016-08-12 15:07:35 +02:00
if ( key_len = = 16 ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_sak_128bits_aes_cmac ( participant - > cak . key ,
2016-08-12 15:07:35 +02:00
context , ctx_len , key ) ;
} else if ( key_len = = 32 ) {
2014-03-25 20:39:02 +01:00
ieee802_1x_sak_128bits_aes_cmac ( participant - > cak . key ,
2016-08-12 15:07:35 +02:00
context , ctx_len , key ) ;
2014-03-25 20:39:02 +01:00
} else {
wpa_printf ( MSG_ERROR , " KaY: SAK Length not support " ) ;
2016-08-12 15:07:35 +02:00
goto fail ;
2014-03-25 20:39:02 +01:00
}
2016-08-12 15:07:35 +02:00
wpa_hexdump ( MSG_DEBUG , " KaY: generated new SAK " , key , key_len ) ;
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 ;
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 ) ;
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 )
peer - > sak_used = FALSE ;
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 ;
Boolean i_is_key_server ;
if ( participant - > is_obliged_key_server ) {
participant - > new_sak = TRUE ;
participant - > to_dist_sak = FALSE ;
ieee802_1x_cp_set_electedself ( kay - > cp , TRUE ) ;
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 */
i_is_key_server = FALSE ;
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 ) ) ;
if ( compare_priorities ( & tmp , key_server ) < 0 )
2014-03-25 20:39:02 +01:00
i_is_key_server = TRUE ;
2016-07-19 11:56:51 +02:00
} else if ( participant - > can_be_key_server ) {
i_is_key_server = TRUE ;
2014-03-25 20:39:02 +01:00
}
if ( i_is_key_server ) {
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 ) ;
}
participant - > is_key_server = TRUE ;
participant - > principal = TRUE ;
participant - > new_sak = TRUE ;
wpa_printf ( MSG_DEBUG , " KaY: I is elected as key server " ) ;
participant - > to_dist_sak = FALSE ;
participant - > is_elected = TRUE ;
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 ) {
2014-03-25 20:39:02 +01: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 ) ;
}
participant - > is_key_server = FALSE ;
participant - > principal = TRUE ;
participant - > is_elected = TRUE ;
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 {
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 ;
Boolean has_peer ;
if ( ! participant - > is_key_server )
return - 1 ;
/* key server self is MACsec-desired and requesting MACsec */
if ( ! kay - > macsec_desired ) {
participant - > advised_desired = FALSE ;
return - 1 ;
}
if ( kay - > macsec_capable = = MACSEC_CAP_NOT_IMPLEMENTED ) {
participant - > advised_desired = FALSE ;
return - 1 ;
}
less_capability = kay - > macsec_capable ;
/* at least one of peers is MACsec-desired and requesting MACsec */
has_peer = FALSE ;
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 ;
2014-03-25 20:39:02 +01:00
has_peer = TRUE ;
}
if ( has_peer ) {
participant - > advised_desired = TRUE ;
participant - > advised_capability = less_capability ;
kay - > authenticated = FALSE ;
kay - > secured = TRUE ;
kay - > failed = FALSE ;
ieee802_1x_cp_connect_secure ( kay - > cp ) ;
ieee802_1x_cp_sm_step ( kay - > cp ) ;
} else {
participant - > advised_desired = FALSE ;
participant - > advised_capability = MACSEC_CAP_NOT_IMPLEMENTED ;
participant - > to_use_sak = FALSE ;
kay - > authenticated = TRUE ;
kay - > secured = FALSE ;
kay - > failed = FALSE ;
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 ) ;
eapol_hdr = wpabuf_put ( pbuf , sizeof ( * eapol_hdr ) ) ;
eapol_hdr - > version = EAPOL_VERSION ;
eapol_hdr - > type = IEEE802_1X_TYPE_EAPOL_MKA ;
eapol_hdr - > length = host_to_be16 ( pbuf - > size - pbuf - > used ) ;
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 ;
}
/**
* 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 ;
wpa_printf ( MSG_DEBUG , " KaY: to enpacket and send the MKPDU " ) ;
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 ) ) {
wpa_printf ( MSG_ERROR , " KaY: encode mkpdu fail! " ) ;
return - 1 ;
}
l2_packet_send ( kay - > l2_mka , NULL , 0 , wpabuf_head ( buf ) , wpabuf_len ( buf ) ) ;
wpabuf_free ( buf ) ;
kay - > active = TRUE ;
participant - > active = TRUE ;
return 0 ;
}
static void ieee802_1x_kay_deinit_transmit_sa ( struct transmit_sa * psa ) ;
/**
* 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 ) ;
Boolean lp_changed ;
struct receive_sc * rxsc , * pre_rxsc ;
struct transmit_sa * txsa , * pre_txsa ;
participant = ( struct ieee802_1x_mka_participant * ) eloop_ctx ;
kay = participant - > kay ;
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 ;
}
}
lp_changed = FALSE ;
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
secy_delete_receive_sc ( kay , rxsc ) ;
ieee802_1x_kay_deinit_receive_sc (
participant , rxsc ) ;
}
}
dl_list_del ( & peer - > list ) ;
os_free ( peer ) ;
lp_changed = TRUE ;
}
}
if ( lp_changed ) {
if ( dl_list_empty ( & participant - > live_peers ) ) {
participant - > advised_desired = FALSE ;
participant - > advised_capability =
MACSEC_CAP_NOT_IMPLEMENTED ;
participant - > to_use_sak = FALSE ;
kay - > authenticated = TRUE ;
kay - > secured = FALSE ;
kay - > failed = FALSE ;
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 ) {
secy_disable_transmit_sa ( kay , txsa ) ;
ieee802_1x_kay_deinit_transmit_sa ( txsa ) ;
}
ieee802_1x_cp_connect_authenticated ( kay - > cp ) ;
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 ) ;
}
}
if ( participant - > new_sak ) {
if ( ! ieee802_1x_kay_generate_new_sak ( participant ) )
participant - > to_dist_sak = TRUE ;
participant - > new_sak = FALSE ;
}
if ( participant - > retry_count < MAX_RETRY_CNT ) {
ieee802_1x_participant_send_mkpdu ( participant ) ;
participant - > retry_count + + ;
}
eloop_register_timeout ( MKA_HELLO_TIME / 1000 , 0 ,
ieee802_1x_participant_timer ,
participant , NULL ) ;
2016-08-15 11:43:42 +02:00
return ;
delete_mka :
kay - > authenticated = FALSE ;
kay - > secured = FALSE ;
kay - > failed = TRUE ;
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 ;
key - > tx_latest = TRUE ;
key - > rx_latest = TRUE ;
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 )
psa - > confidentiality = TRUE ;
else
psa - > confidentiality = FALSE ;
psa - > an = an ;
psa - > pkey = key ;
psa - > next_pn = next_PN ;
psa - > sc = psc ;
os_get_time ( & psa - > created_time ) ;
psa - > in_use = FALSE ;
dl_list_add ( & psc - > sa_list , & psa - > list ) ;
wpa_printf ( MSG_DEBUG ,
2016-08-12 15:07:33 +02:00
" KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d) " ,
an , next_PN , psc - > channel ) ;
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 )
{
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 *
ieee802_1x_kay_init_transmit_sc ( const struct ieee802_1x_mka_sci * sci ,
int channel )
{
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 ) ) ;
psc - > channel = channel ;
os_get_time ( & psc - > created_time ) ;
psc - > transmitting = FALSE ;
psc - > encoding_sa = FALSE ;
psc - > enciphering_sa = FALSE ;
dl_list_init ( & psc - > sa_list ) ;
wpa_printf ( MSG_DEBUG , " KaY: Create transmit SC(channel: %d) " , channel ) ;
wpa_hexdump ( MSG_DEBUG , " SCI: " , ( u8 * ) sci , sizeof ( * sci ) ) ;
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 ;
wpa_printf ( MSG_DEBUG , " KaY: Delete transmit SC(channel: %d) " ,
psc - > channel ) ;
dl_list_for_each_safe ( psa , tmp , & psc - > sa_list , struct transmit_sa ,
list ) {
secy_disable_transmit_sa ( participant - > kay , psa ) ;
ieee802_1x_kay_deinit_transmit_sa ( psa ) ;
}
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 ,
Boolean ltx , Boolean lrx )
{
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 ,
u8 oan , Boolean otx , Boolean orx )
{
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 ;
}
/**
* 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 ) ) {
sa_key - > rx_latest = TRUE ;
sa_key - > tx_latest = TRUE ;
latest_sak = sa_key ;
principal - > to_use_sak = TRUE ;
} else {
sa_key - > rx_latest = FALSE ;
sa_key - > tx_latest = FALSE ;
}
}
if ( ! latest_sak ) {
wpa_printf ( MSG_ERROR , " lki related sak not found " ) ;
return - 1 ;
}
dl_list_for_each ( rxsc , & principal - > rxsc_list , struct receive_sc , list ) {
rxsa = ieee802_1x_kay_init_receive_sa ( rxsc , latest_sak - > an , 1 ,
latest_sak ) ;
if ( ! rxsa )
return - 1 ;
secy_create_receive_sa ( kay , rxsa ) ;
}
txsa = ieee802_1x_kay_init_transmit_sa ( principal - > txsc , latest_sak - > an ,
1 , latest_sak ) ;
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 ) {
if ( is_ki_equal ( & txsa - > pkey - > key_identifier , ki ) ) {
secy_disable_transmit_sa ( kay , txsa ) ;
ieee802_1x_kay_deinit_transmit_sa ( txsa ) ;
}
}
/* 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 ) {
if ( is_ki_equal ( & rxsa - > pkey - > key_identifier , ki ) ) {
secy_disable_receive_sa ( kay , rxsa ) ;
ieee802_1x_kay_deinit_receive_sa ( rxsa ) ;
}
}
}
/* 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 ) ) {
ieee802_1x_kay_deinit_data_key ( sa_key ) ;
break ;
}
if ( principal - > new_key = = sa_key )
principal - > new_key = NULL ;
}
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 ) ) {
txsa - > in_use = TRUE ;
secy_enable_transmit_sa ( kay , txsa ) ;
ieee802_1x_cp_set_usingtransmitas (
principal - > kay - > cp , TRUE ) ;
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 ) ) {
rxsa - > in_use = TRUE ;
secy_enable_receive_sa ( kay , rxsa ) ;
ieee802_1x_cp_set_usingreceivesas (
principal - > kay - > cp , TRUE ) ;
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 ;
if ( principal - > retry_count < MAX_RETRY_CNT ) {
ieee802_1x_participant_send_mkpdu ( principal ) ;
principal - > retry_count + + ;
}
return 0 ;
}
/**
* ieee802_1x_kay_mkpdu_sanity_check -
* sanity check specified in clause 11.11 .2 of IEEE802 .1 X - 2010
*/
static int ieee802_1x_kay_mkpdu_sanity_check ( struct ieee802_1x_kay * kay ,
const u8 * buf , size_t len )
{
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 ;
u8 icv [ MAX_ICV_LEN ] ;
u8 * msg_icv ;
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 ) ;
/* destination address should be not individual address */
if ( os_memcmp ( eth_hdr - > dest , pae_group_addr , ETH_ALEN ) ! = 0 ) {
wpa_printf ( MSG_MSGDUMP ,
" KaY: ethernet destination address is not PAE group address " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
/* MKPDU should 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 ) {
wpa_printf ( MSG_MSGDUMP , " KaY: MKPDU is less than 32 octets " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
/* MKPDU should be a multiple of 4 octets */
2014-03-25 20:39:02 +01:00
if ( ( mka_msg_len % 4 ) ! = 0 ) {
wpa_printf ( MSG_MSGDUMP ,
" KaY: MKPDU is not multiple of 4 octets " ) ;
return - 1 ;
}
body = ( struct ieee802_1x_mka_basic_body * ) mka_hdr ;
ieee802_1x_mka_dump_basic_body ( body ) ;
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 ;
}
/* CKN should be owned by I */
participant = ieee802_1x_kay_get_participant ( kay , body - > ckn ) ;
if ( ! participant ) {
wpa_printf ( MSG_DEBUG , " CKN is not included in my CA " ) ;
return - 1 ;
}
/* algorithm agility check */
if ( os_memcmp ( body - > algo_agility , mka_algo_agility ,
sizeof ( body - > algo_agility ) ) ! = 0 ) {
wpa_printf ( MSG_ERROR ,
" KaY: peer's algorithm agility not supported for me " ) ;
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 .
*/
if ( mka_alg_tbl [ kay - > mka_algindex ] . icv_hash (
participant - > ick . key ,
buf , len - mka_alg_tbl [ kay - > mka_algindex ] . icv_len , icv ) ) {
wpa_printf ( MSG_ERROR , " KaY: omac1_aes_128 failed " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
2014-03-25 20:39:02 +01:00
msg_icv = ieee802_1x_mka_decode_icv_body ( participant , ( u8 * ) mka_hdr ,
mka_msg_len ) ;
2016-08-15 11:43:42 +02:00
if ( ! msg_icv ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_ERROR , " KaY: No ICV " ) ;
return - 1 ;
}
2016-08-15 11:43:42 +02:00
if ( os_memcmp_const ( msg_icv , icv ,
mka_alg_tbl [ kay - > mka_algindex ] . icv_len ) ! = 0 ) {
wpa_printf ( MSG_ERROR ,
" KaY: Computed ICV is not equal to Received ICV " ) ;
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 ;
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 ;
Boolean handled [ 256 ] ;
if ( ieee802_1x_kay_mkpdu_sanity_check ( kay , buf , len ) )
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 ;
body_len = get_mka_param_body_len ( hdr ) ;
pos + = body_len + MKA_HDR_LEN ;
left_len - = body_len + MKA_HDR_LEN ;
/* check i am in the peer's peer list */
2016-08-15 11:43:42 +02:00
if ( ieee802_1x_mka_i_in_peerlist ( participant , pos , left_len ) & &
! ieee802_1x_kay_is_in_live_peer ( participant ,
participant - > current_peer_id . mi ) ) {
2014-03-25 20:39:02 +01:00
/* accept the peer as live peer */
if ( ieee802_1x_kay_is_in_potential_peer (
participant , participant - > current_peer_id . mi ) ) {
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 + + )
handled [ i ] = FALSE ;
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 ;
body_len = get_mka_param_body_len ( hdr ) ;
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 ) ;
2016-08-12 15:07:33 +02:00
continue ;
2014-03-25 20:39:02 +01:00
}
if ( handled [ body_type ] )
2016-08-12 15:07:33 +02:00
continue ;
2014-03-25 20:39:02 +01: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 ) {
mka_body_handler [ body_type ] . body_rx
2014-03-25 20:39:02 +01:00
( participant , pos , left_len ) ;
} else {
wpa_printf ( MSG_ERROR ,
2016-08-12 15:07:33 +02:00
" The type %d is not supported in this MKA version %d " ,
2014-03-25 20:39:02 +01:00
body_type , MKA_VERSION_ID ) ;
}
}
kay - > active = TRUE ;
participant - > retry_count = 0 ;
participant - > active = TRUE ;
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 ;
/* 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 ) ;
if ( len ! = sizeof ( * eth_hdr ) + sizeof ( * eapol_hdr ) +
2016-06-24 00:38:48 +02:00
be_to_host16 ( eapol_hdr - > length ) ) {
2014-03-25 20:39:02 +01:00
wpa_printf ( MSG_MSGDUMP , " KAY: EAPOL MPDU is invalid: (%lu-%lu) " ,
( unsigned long ) len ,
2016-06-24 00:38:48 +02:00
( unsigned long ) be_to_host16 ( eapol_hdr - > length ) ) ;
2014-03-25 20:39:02 +01:00
return ;
}
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 )
return ;
wpa_hexdump ( MSG_DEBUG , " RX EAPOL-MKA: " , buf , len ) ;
if ( dl_list_empty ( & kay - > participant_list ) ) {
wpa_printf ( MSG_ERROR , " KaY: no MKA participant instance " ) ;
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 ,
const char * ifname , const u8 * addr )
{
struct ieee802_1x_kay * kay ;
kay = os_zalloc ( sizeof ( * kay ) ) ;
if ( ! kay ) {
wpa_printf ( MSG_ERROR , " KaY-%s: out of memory " , __func__ ) ;
return NULL ;
}
kay - > ctx = ctx ;
kay - > enable = TRUE ;
kay - > active = FALSE ;
kay - > authenticated = FALSE ;
kay - > secured = FALSE ;
kay - > failed = FALSE ;
kay - > policy = policy ;
os_strlcpy ( kay - > if_name , ifname , IFNAMSIZ ) ;
os_memcpy ( kay - > actor_sci . addr , addr , ETH_ALEN ) ;
2016-06-24 16:48:27 +02:00
kay - > actor_sci . port = host_to_be16 ( 0x0001 ) ;
2014-03-25 20:39:02 +01:00
kay - > actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER ;
/* 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 ) ;
if ( policy = = DO_NOT_SECURE ) {
kay - > macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED ;
kay - > macsec_desired = FALSE ;
kay - > macsec_protect = FALSE ;
2014-10-30 10:43:47 +01:00
kay - > macsec_validate = Disabled ;
2014-03-25 20:39:02 +01:00
kay - > macsec_replay_protect = FALSE ;
kay - > macsec_replay_window = 0 ;
kay - > macsec_confidentiality = CONFIDENTIALITY_NONE ;
} else {
kay - > macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50 ;
kay - > macsec_desired = TRUE ;
kay - > macsec_protect = TRUE ;
2014-10-30 10:43:47 +01:00
kay - > macsec_validate = Strict ;
2014-03-25 20:39:02 +01:00
kay - > macsec_replay_protect = FALSE ;
kay - > macsec_replay_window = 0 ;
kay - > macsec_confidentiality = CONFIDENTIALITY_OFFSET_0 ;
}
wpa_printf ( MSG_DEBUG , " KaY: state machine created " ) ;
/* Initialize the SecY must be prio to CP, as CP will control SecY */
secy_init_macsec ( kay ) ;
secy_get_available_transmit_sc ( kay , & kay - > sc_ch ) ;
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 ) ;
2014-03-25 20:39:02 +01:00
if ( kay - > cp = = NULL ) {
ieee802_1x_kay_deinit ( kay ) ;
return NULL ;
}
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 " ) ;
ieee802_1x_kay_deinit ( kay ) ;
return NULL ;
}
}
return kay ;
}
/**
* 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 ) ;
}
/**
* ieee802_1x_kay_create_mka -
*/
struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka ( struct ieee802_1x_kay * kay , struct mka_key_name * ckn ,
struct mka_key * cak , u32 life ,
enum mka_created_mode mode , Boolean is_authenticator )
{
struct ieee802_1x_mka_participant * participant ;
unsigned int usecs ;
if ( ! kay | | ! ckn | | ! cak ) {
wpa_printf ( MSG_ERROR , " KaY: ckn or cak is null " ) ;
return NULL ;
}
if ( cak - > len ! = mka_alg_tbl [ kay - > mka_algindex ] . cak_len ) {
wpa_printf ( MSG_ERROR , " KaY: CAK length not follow key schema " ) ;
return NULL ;
}
if ( ckn - > len > MAX_CKN_LEN ) {
wpa_printf ( MSG_ERROR , " KaY: CKN is out of range(<=32 bytes) " ) ;
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 ) ;
participant - > cak . len = cak - > len ;
os_memcpy ( participant - > cak . key , cak - > key , cak - > len ) ;
if ( life )
participant - > cak_life = life + time ( NULL ) ;
switch ( mode ) {
case EAP_EXCHANGE :
if ( is_authenticator ) {
participant - > is_obliged_key_server = TRUE ;
participant - > can_be_key_server = TRUE ;
participant - > is_key_server = TRUE ;
participant - > principal = TRUE ;
os_memcpy ( & kay - > key_server_sci , & kay - > actor_sci ,
sizeof ( kay - > key_server_sci ) ) ;
kay - > key_server_priority = kay - > actor_priority ;
participant - > is_elected = TRUE ;
} else {
participant - > is_obliged_key_server = FALSE ;
participant - > can_be_key_server = FALSE ;
participant - > is_key_server = FALSE ;
participant - > is_elected = TRUE ;
}
break ;
default :
participant - > is_obliged_key_server = FALSE ;
participant - > can_be_key_server = TRUE ;
2016-07-19 11:56:51 +02:00
participant - > is_key_server = TRUE ;
2014-03-25 20:39:02 +01:00
participant - > is_elected = FALSE ;
break ;
}
participant - > cached = FALSE ;
participant - > active = FALSE ;
participant - > participant = FALSE ;
participant - > retain = FALSE ;
participant - > activate = DEFAULT ;
if ( participant - > is_key_server )
participant - > principal = TRUE ;
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 ;
2014-03-25 20:39:02 +01: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 ;
dl_list_init ( & participant - > sak_list ) ;
participant - > new_key = NULL ;
dl_list_init ( & participant - > rxsc_list ) ;
participant - > txsc = ieee802_1x_kay_init_transmit_sc ( & kay - > actor_sci ,
kay - > sc_ch ) ;
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 ) ;
2014-03-25 20:39:02 +01:00
secy_create_transmit_sc ( kay , participant - > txsc ) ;
/* to derive KEK from CAK and CKN */
participant - > kek . len = mka_alg_tbl [ kay - > mka_algindex ] . kek_len ;
if ( mka_alg_tbl [ kay - > mka_algindex ] . kek_trfm ( participant - > cak . key ,
participant - > ckn . name ,
participant - > ckn . len ,
participant - > kek . key ) ) {
wpa_printf ( MSG_ERROR , " KaY: Derived KEK failed " ) ;
goto fail ;
}
wpa_hexdump_key ( MSG_DEBUG , " KaY: Derived KEK " ,
participant - > kek . key , participant - > kek . len ) ;
/* to derive ICK from CAK and CKN */
participant - > ick . len = mka_alg_tbl [ kay - > mka_algindex ] . ick_len ;
if ( mka_alg_tbl [ kay - > mka_algindex ] . ick_trfm ( participant - > cak . key ,
participant - > ckn . name ,
participant - > ckn . len ,
participant - > ick . key ) ) {
wpa_printf ( MSG_ERROR , " KaY: Derived ICK failed " ) ;
goto fail ;
}
wpa_hexdump_key ( MSG_DEBUG , " KaY: Derived ICK " ,
participant - > ick . key , participant - > ick . len ) ;
dl_list_add ( & kay - > participant_list , & participant - > list ) ;
wpa_hexdump ( MSG_DEBUG , " KaY: Participant created: " ,
ckn - > name , ckn - > len ) ;
usecs = os_random ( ) % ( MKA_HELLO_TIME * 1000 ) ;
eloop_register_timeout ( 0 , usecs , ieee802_1x_participant_timer ,
participant , NULL ) ;
participant - > mka_life = MKA_LIFE_TIME / 1000 + time ( NULL ) +
usecs / 1000000 ;
return participant ;
fail :
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 */
participant = ieee802_1x_kay_get_participant ( kay , ckn - > name ) ;
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 ) ;
os_free ( sak - > key ) ;
os_free ( sak ) ;
}
while ( ! dl_list_empty ( & participant - > rxsc_list ) ) {
rxsc = dl_list_entry ( participant - > rxsc_list . next ,
struct receive_sc , list ) ;
secy_delete_receive_sc ( kay , rxsc ) ;
ieee802_1x_kay_deinit_receive_sc ( participant , rxsc ) ;
}
secy_delete_transmit_sc ( kay , participant - > txsc ) ;
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 ,
struct mka_key_name * ckn ,
Boolean status )
{
struct ieee802_1x_mka_participant * participant ;
if ( ! kay | | ! ckn )
return ;
participant = ieee802_1x_kay_get_participant ( kay , ckn - > name ) ;
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 ;
participant - > new_sak = TRUE ;
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 ;
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 )
kay - > macsec_desired = FALSE ;
kay - > macsec_csindex = cs_index ;
kay - > macsec_capable = cipher_suite_tbl [ kay - > macsec_csindex ] . capable ;
participant = ieee802_1x_kay_get_principal_participant ( kay ) ;
if ( participant ) {
wpa_printf ( MSG_INFO , " KaY: Cipher Suite changed " ) ;
participant - > new_sak = TRUE ;
}
return 0 ;
}