15b9c547cSRui Paulo /* 2*4bc52338SCy Schubert * IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine 35b9c547cSRui Paulo * Copyright (c) 2013, Qualcomm Atheros, Inc. 45b9c547cSRui Paulo * 55b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 65b9c547cSRui Paulo * See README for more details. 75b9c547cSRui Paulo */ 85b9c547cSRui Paulo 95b9c547cSRui Paulo #include <time.h> 105b9c547cSRui Paulo #include "includes.h" 115b9c547cSRui Paulo #include "common.h" 125b9c547cSRui Paulo #include "list.h" 135b9c547cSRui Paulo #include "eloop.h" 145b9c547cSRui Paulo #include "wpabuf.h" 155b9c547cSRui Paulo #include "state_machine.h" 165b9c547cSRui Paulo #include "l2_packet/l2_packet.h" 175b9c547cSRui Paulo #include "common/eapol_common.h" 185b9c547cSRui Paulo #include "crypto/aes_wrap.h" 195b9c547cSRui Paulo #include "ieee802_1x_cp.h" 205b9c547cSRui Paulo #include "ieee802_1x_key.h" 215b9c547cSRui Paulo #include "ieee802_1x_kay.h" 225b9c547cSRui Paulo #include "ieee802_1x_kay_i.h" 235b9c547cSRui Paulo #include "ieee802_1x_secy_ops.h" 245b9c547cSRui Paulo 255b9c547cSRui Paulo 265b9c547cSRui Paulo #define DEFAULT_SA_KEY_LEN 16 275b9c547cSRui Paulo #define DEFAULT_ICV_LEN 16 285b9c547cSRui Paulo #define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */ 295b9c547cSRui Paulo 30*4bc52338SCy Schubert #define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without 31*4bc52338SCy Schubert * SAK-USE before dropping */ 32*4bc52338SCy Schubert 335b9c547cSRui Paulo #define PENDING_PN_EXHAUSTION 0xC0000000 345b9c547cSRui Paulo 35780fb4a2SCy Schubert #define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3) 36780fb4a2SCy Schubert 375b9c547cSRui Paulo /* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */ 385b9c547cSRui Paulo #define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 } 395b9c547cSRui Paulo static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; 405b9c547cSRui Paulo 415b9c547cSRui Paulo /* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */ 425b9c547cSRui Paulo static struct macsec_ciphersuite cipher_suite_tbl[] = { 435b9c547cSRui Paulo /* GCM-AES-128 */ 445b9c547cSRui Paulo { 45780fb4a2SCy Schubert .id = CS_ID_GCM_AES_128, 46780fb4a2SCy Schubert .name = CS_NAME_GCM_AES_128, 47780fb4a2SCy Schubert .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, 48780fb4a2SCy Schubert .sak_len = DEFAULT_SA_KEY_LEN, 495b9c547cSRui Paulo }, 5085732ac8SCy Schubert /* GCM-AES-256 */ 5185732ac8SCy Schubert { 5285732ac8SCy Schubert .id = CS_ID_GCM_AES_256, 5385732ac8SCy Schubert .name = CS_NAME_GCM_AES_256, 5485732ac8SCy Schubert .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, 5585732ac8SCy Schubert .sak_len = 32, 5685732ac8SCy Schubert }, 575b9c547cSRui Paulo }; 585b9c547cSRui Paulo #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) 595b9c547cSRui Paulo #define DEFAULT_CS_INDEX 0 605b9c547cSRui Paulo 615b9c547cSRui Paulo static struct mka_alg mka_alg_tbl[] = { 625b9c547cSRui Paulo { 63780fb4a2SCy Schubert .parameter = MKA_ALGO_AGILITY_2009, 645b9c547cSRui Paulo 65780fb4a2SCy Schubert .icv_len = DEFAULT_ICV_LEN, 66780fb4a2SCy Schubert 67*4bc52338SCy Schubert .cak_trfm = ieee802_1x_cak_aes_cmac, 68*4bc52338SCy Schubert .ckn_trfm = ieee802_1x_ckn_aes_cmac, 69*4bc52338SCy Schubert .kek_trfm = ieee802_1x_kek_aes_cmac, 70*4bc52338SCy Schubert .ick_trfm = ieee802_1x_ick_aes_cmac, 71*4bc52338SCy Schubert .icv_hash = ieee802_1x_icv_aes_cmac, 725b9c547cSRui Paulo }, 735b9c547cSRui Paulo }; 745b9c547cSRui Paulo #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) 755b9c547cSRui Paulo 765b9c547cSRui Paulo 775b9c547cSRui Paulo static int is_ki_equal(struct ieee802_1x_mka_ki *ki1, 785b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki2) 795b9c547cSRui Paulo { 805b9c547cSRui Paulo return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 && 815b9c547cSRui Paulo ki1->kn == ki2->kn; 825b9c547cSRui Paulo } 835b9c547cSRui Paulo 845b9c547cSRui Paulo 855b9c547cSRui Paulo static void set_mka_param_body_len(void *body, unsigned int len) 865b9c547cSRui Paulo { 875b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr = body; 885b9c547cSRui Paulo hdr->length = (len >> 8) & 0x0f; 895b9c547cSRui Paulo hdr->length1 = len & 0xff; 905b9c547cSRui Paulo } 915b9c547cSRui Paulo 925b9c547cSRui Paulo 935b9c547cSRui Paulo static unsigned int get_mka_param_body_len(const void *body) 945b9c547cSRui Paulo { 955b9c547cSRui Paulo const struct ieee802_1x_mka_hdr *hdr = body; 965b9c547cSRui Paulo return (hdr->length << 8) | hdr->length1; 975b9c547cSRui Paulo } 985b9c547cSRui Paulo 995b9c547cSRui Paulo 100780fb4a2SCy Schubert static u8 get_mka_param_body_type(const void *body) 1015b9c547cSRui Paulo { 1025b9c547cSRui Paulo const struct ieee802_1x_mka_hdr *hdr = body; 1035b9c547cSRui Paulo return hdr->type; 1045b9c547cSRui Paulo } 1055b9c547cSRui Paulo 1065b9c547cSRui Paulo 107*4bc52338SCy Schubert static const char * mi_txt(const u8 *mi) 108*4bc52338SCy Schubert { 109*4bc52338SCy Schubert static char txt[MI_LEN * 2 + 1]; 110*4bc52338SCy Schubert 111*4bc52338SCy Schubert wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN); 112*4bc52338SCy Schubert return txt; 113*4bc52338SCy Schubert } 114*4bc52338SCy Schubert 115*4bc52338SCy Schubert 116*4bc52338SCy Schubert static const char * sci_txt(const struct ieee802_1x_mka_sci *sci) 117*4bc52338SCy Schubert { 118*4bc52338SCy Schubert static char txt[ETH_ALEN * 3 + 1 + 5 + 1]; 119*4bc52338SCy Schubert 120*4bc52338SCy Schubert os_snprintf(txt, sizeof(txt), MACSTR "@%u", 121*4bc52338SCy Schubert MAC2STR(sci->addr), be_to_host16(sci->port)); 122*4bc52338SCy Schubert return txt; 123*4bc52338SCy Schubert } 124*4bc52338SCy Schubert 125*4bc52338SCy Schubert 126*4bc52338SCy Schubert static const char * algo_agility_txt(const u8 *algo_agility) 127*4bc52338SCy Schubert { 128*4bc52338SCy Schubert static char txt[4 * 2 + 1]; 129*4bc52338SCy Schubert 130*4bc52338SCy Schubert wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4); 131*4bc52338SCy Schubert return txt; 132*4bc52338SCy Schubert } 133*4bc52338SCy Schubert 134*4bc52338SCy Schubert 1355b9c547cSRui Paulo /** 1365b9c547cSRui Paulo * ieee802_1x_mka_dump_basic_body - 1375b9c547cSRui Paulo */ 1385b9c547cSRui Paulo static void 1395b9c547cSRui Paulo ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) 1405b9c547cSRui Paulo { 1415b9c547cSRui Paulo size_t body_len; 1425b9c547cSRui Paulo 1435b9c547cSRui Paulo if (!body) 1445b9c547cSRui Paulo return; 1455b9c547cSRui Paulo 146*4bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-8 */ 1475b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 148*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set"); 149*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version); 150*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority); 151*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server); 152*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired); 153*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d", 154*4bc52338SCy Schubert body->macsec_capability); 155*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len); 156*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci)); 157*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s", 158*4bc52338SCy Schubert mi_txt(body->actor_mi)); 159*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d", 1605b9c547cSRui Paulo be_to_host32(body->actor_mn)); 161*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s", 162*4bc52338SCy Schubert algo_agility_txt(body->algo_agility)); 163*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn, 1645b9c547cSRui Paulo body_len + MKA_HDR_LEN - sizeof(*body)); 1655b9c547cSRui Paulo } 1665b9c547cSRui Paulo 1675b9c547cSRui Paulo 1685b9c547cSRui Paulo /** 1695b9c547cSRui Paulo * ieee802_1x_mka_dump_peer_body - 1705b9c547cSRui Paulo */ 1715b9c547cSRui Paulo static void 1725b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) 1735b9c547cSRui Paulo { 1745b9c547cSRui Paulo size_t body_len; 1755b9c547cSRui Paulo size_t i; 1765b9c547cSRui Paulo u8 *mi; 177780fb4a2SCy Schubert be32 mn; 1785b9c547cSRui Paulo 1795b9c547cSRui Paulo if (body == NULL) 1805b9c547cSRui Paulo return; 1815b9c547cSRui Paulo 182*4bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-9 */ 1835b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 1845b9c547cSRui Paulo if (body->type == MKA_LIVE_PEER_LIST) { 185*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Live Peer List parameter set"); 186*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); 1875b9c547cSRui Paulo } else if (body->type == MKA_POTENTIAL_PEER_LIST) { 188*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Potential Peer List parameter set"); 189*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); 1905b9c547cSRui Paulo } 1915b9c547cSRui Paulo 1925b9c547cSRui Paulo for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) { 1935b9c547cSRui Paulo mi = body->peer + i; 1945b9c547cSRui Paulo os_memcpy(&mn, mi + MI_LEN, sizeof(mn)); 195*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d", 196*4bc52338SCy Schubert mi_txt(mi), be_to_host32(mn)); 1975b9c547cSRui Paulo } 1985b9c547cSRui Paulo } 1995b9c547cSRui Paulo 2005b9c547cSRui Paulo 2015b9c547cSRui Paulo /** 2025b9c547cSRui Paulo * ieee802_1x_mka_dump_dist_sak_body - 2035b9c547cSRui Paulo */ 2045b9c547cSRui Paulo static void 2055b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) 2065b9c547cSRui Paulo { 2075b9c547cSRui Paulo size_t body_len; 2085b9c547cSRui Paulo 2095b9c547cSRui Paulo if (body == NULL) 2105b9c547cSRui Paulo return; 2115b9c547cSRui Paulo 212*4bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */ 2135b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 214*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Distributed SAK parameter set"); 215*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan); 216*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d", 2175b9c547cSRui Paulo body->confid_offset); 218*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len); 2195b9c547cSRui Paulo if (!body_len) 2205b9c547cSRui Paulo return; 2215b9c547cSRui Paulo 222*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Number............: %d", 2235b9c547cSRui Paulo be_to_host32(body->kn)); 224*4bc52338SCy Schubert /* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */ 225*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24); 2265b9c547cSRui Paulo } 2275b9c547cSRui Paulo 2285b9c547cSRui Paulo 2295b9c547cSRui Paulo static const char * yes_no(int val) 2305b9c547cSRui Paulo { 2315b9c547cSRui Paulo return val ? "Yes" : "No"; 2325b9c547cSRui Paulo } 2335b9c547cSRui Paulo 2345b9c547cSRui Paulo 2355b9c547cSRui Paulo /** 2365b9c547cSRui Paulo * ieee802_1x_mka_dump_sak_use_body - 2375b9c547cSRui Paulo */ 2385b9c547cSRui Paulo static void 2395b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) 2405b9c547cSRui Paulo { 2415b9c547cSRui Paulo int body_len; 2425b9c547cSRui Paulo 2435b9c547cSRui Paulo if (body == NULL) 2445b9c547cSRui Paulo return; 2455b9c547cSRui Paulo 246*4bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-10 */ 2475b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 248*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set"); 2495b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan); 2505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx)); 2515b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx)); 252*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan); 253*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx)); 254*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx)); 255*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx)); 256*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx)); 2575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s", 2585b9c547cSRui Paulo yes_no(body->delay_protect)); 2595b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len); 2605b9c547cSRui Paulo if (!body_len) 2615b9c547cSRui Paulo return; 2625b9c547cSRui Paulo 263*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi)); 2645b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tKey Number.......: %u", 2655b9c547cSRui Paulo be_to_host32(body->lkn)); 2665b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLowest PN........: %u", 2675b9c547cSRui Paulo be_to_host32(body->llpn)); 268*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi)); 269*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u", 2705b9c547cSRui Paulo be_to_host32(body->okn)); 271*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u", 2725b9c547cSRui Paulo be_to_host32(body->olpn)); 2735b9c547cSRui Paulo } 2745b9c547cSRui Paulo 2755b9c547cSRui Paulo 2765b9c547cSRui Paulo /** 2775b9c547cSRui Paulo * ieee802_1x_kay_get_participant - 2785b9c547cSRui Paulo */ 2795b9c547cSRui Paulo static struct ieee802_1x_mka_participant * 28085732ac8SCy Schubert ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn, 28185732ac8SCy Schubert size_t len) 2825b9c547cSRui Paulo { 2835b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 2845b9c547cSRui Paulo 2855b9c547cSRui Paulo dl_list_for_each(participant, &kay->participant_list, 2865b9c547cSRui Paulo struct ieee802_1x_mka_participant, list) { 28785732ac8SCy Schubert if (participant->ckn.len == len && 28885732ac8SCy Schubert os_memcmp(participant->ckn.name, ckn, 2895b9c547cSRui Paulo participant->ckn.len) == 0) 2905b9c547cSRui Paulo return participant; 2915b9c547cSRui Paulo } 2925b9c547cSRui Paulo 2935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: participant is not found"); 2945b9c547cSRui Paulo 2955b9c547cSRui Paulo return NULL; 2965b9c547cSRui Paulo } 2975b9c547cSRui Paulo 2985b9c547cSRui Paulo 2995b9c547cSRui Paulo /** 3005b9c547cSRui Paulo * ieee802_1x_kay_get_principal_participant - 3015b9c547cSRui Paulo */ 3025b9c547cSRui Paulo static struct ieee802_1x_mka_participant * 3035b9c547cSRui Paulo ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay) 3045b9c547cSRui Paulo { 3055b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 3065b9c547cSRui Paulo 3075b9c547cSRui Paulo dl_list_for_each(participant, &kay->participant_list, 3085b9c547cSRui Paulo struct ieee802_1x_mka_participant, list) { 3095b9c547cSRui Paulo if (participant->principal) 3105b9c547cSRui Paulo return participant; 3115b9c547cSRui Paulo } 3125b9c547cSRui Paulo 313780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: principal participant is not found"); 3145b9c547cSRui Paulo return NULL; 3155b9c547cSRui Paulo } 3165b9c547cSRui Paulo 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers, 3195b9c547cSRui Paulo const u8 *mi) 3205b9c547cSRui Paulo { 3215b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 3225b9c547cSRui Paulo 3235b9c547cSRui Paulo dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) { 3245b9c547cSRui Paulo if (os_memcmp(peer->mi, mi, MI_LEN) == 0) 3255b9c547cSRui Paulo return peer; 3265b9c547cSRui Paulo } 3275b9c547cSRui Paulo 3285b9c547cSRui Paulo return NULL; 3295b9c547cSRui Paulo } 3305b9c547cSRui Paulo 3315b9c547cSRui Paulo 3325b9c547cSRui Paulo /** 333780fb4a2SCy Schubert * ieee802_1x_kay_get_potential_peer 3345b9c547cSRui Paulo */ 3355b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 336780fb4a2SCy Schubert ieee802_1x_kay_get_potential_peer( 337780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi) 3385b9c547cSRui Paulo { 3395b9c547cSRui Paulo return get_peer_mi(&participant->potential_peers, mi); 3405b9c547cSRui Paulo } 3415b9c547cSRui Paulo 3425b9c547cSRui Paulo 3435b9c547cSRui Paulo /** 3445b9c547cSRui Paulo * ieee802_1x_kay_get_live_peer 3455b9c547cSRui Paulo */ 3465b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 3475b9c547cSRui Paulo ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, 3485b9c547cSRui Paulo const u8 *mi) 3495b9c547cSRui Paulo { 3505b9c547cSRui Paulo return get_peer_mi(&participant->live_peers, mi); 3515b9c547cSRui Paulo } 3525b9c547cSRui Paulo 3535b9c547cSRui Paulo 3545b9c547cSRui Paulo /** 355780fb4a2SCy Schubert * ieee802_1x_kay_is_in_potential_peer 356780fb4a2SCy Schubert */ 357780fb4a2SCy Schubert static Boolean 358780fb4a2SCy Schubert ieee802_1x_kay_is_in_potential_peer( 359780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi) 360780fb4a2SCy Schubert { 361780fb4a2SCy Schubert return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL; 362780fb4a2SCy Schubert } 363780fb4a2SCy Schubert 364780fb4a2SCy Schubert 365780fb4a2SCy Schubert /** 366780fb4a2SCy Schubert * ieee802_1x_kay_is_in_live_peer 367780fb4a2SCy Schubert */ 368780fb4a2SCy Schubert static Boolean 369780fb4a2SCy Schubert ieee802_1x_kay_is_in_live_peer( 370780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi) 371780fb4a2SCy Schubert { 372780fb4a2SCy Schubert return ieee802_1x_kay_get_live_peer(participant, mi) != NULL; 373780fb4a2SCy Schubert } 374780fb4a2SCy Schubert 375780fb4a2SCy Schubert 376780fb4a2SCy Schubert /** 377780fb4a2SCy Schubert * ieee802_1x_kay_get_peer 378780fb4a2SCy Schubert */ 379780fb4a2SCy Schubert static struct ieee802_1x_kay_peer * 380780fb4a2SCy Schubert ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, 381780fb4a2SCy Schubert const u8 *mi) 382780fb4a2SCy Schubert { 383780fb4a2SCy Schubert struct ieee802_1x_kay_peer *peer; 384780fb4a2SCy Schubert 385780fb4a2SCy Schubert peer = ieee802_1x_kay_get_live_peer(participant, mi); 386780fb4a2SCy Schubert if (peer) 387780fb4a2SCy Schubert return peer; 388780fb4a2SCy Schubert 389780fb4a2SCy Schubert return ieee802_1x_kay_get_potential_peer(participant, mi); 390780fb4a2SCy Schubert } 391780fb4a2SCy Schubert 392780fb4a2SCy Schubert 393780fb4a2SCy Schubert /** 3945b9c547cSRui Paulo * ieee802_1x_kay_get_cipher_suite 3955b9c547cSRui Paulo */ 3965b9c547cSRui Paulo static struct macsec_ciphersuite * 3975b9c547cSRui Paulo ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, 398*4bc52338SCy Schubert const u8 *cs_id, unsigned int *idx) 3995b9c547cSRui Paulo { 4005b9c547cSRui Paulo unsigned int i; 401780fb4a2SCy Schubert u64 cs; 402780fb4a2SCy Schubert be64 _cs; 403780fb4a2SCy Schubert 404780fb4a2SCy Schubert os_memcpy(&_cs, cs_id, CS_ID_LEN); 405780fb4a2SCy Schubert cs = be_to_host64(_cs); 4065b9c547cSRui Paulo 4075b9c547cSRui Paulo for (i = 0; i < CS_TABLE_SIZE; i++) { 408*4bc52338SCy Schubert if (cipher_suite_tbl[i].id == cs) { 409*4bc52338SCy Schubert *idx = i; 4105b9c547cSRui Paulo return &cipher_suite_tbl[i]; 4115b9c547cSRui Paulo } 412*4bc52338SCy Schubert } 4135b9c547cSRui Paulo 414780fb4a2SCy Schubert return NULL; 415780fb4a2SCy Schubert } 416780fb4a2SCy Schubert 417780fb4a2SCy Schubert 41885732ac8SCy Schubert u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci) 41985732ac8SCy Schubert { 42085732ac8SCy Schubert struct ieee802_1x_mka_sci tmp; 42185732ac8SCy Schubert 42285732ac8SCy Schubert os_memcpy(tmp.addr, sci->addr, ETH_ALEN); 42385732ac8SCy Schubert tmp.port = sci->port; 42485732ac8SCy Schubert 42585732ac8SCy Schubert return *((u64 *) &tmp); 42685732ac8SCy Schubert } 42785732ac8SCy Schubert 42885732ac8SCy Schubert 429780fb4a2SCy Schubert static Boolean sci_equal(const struct ieee802_1x_mka_sci *a, 430780fb4a2SCy Schubert const struct ieee802_1x_mka_sci *b) 431780fb4a2SCy Schubert { 432780fb4a2SCy Schubert return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0; 433780fb4a2SCy Schubert } 434780fb4a2SCy Schubert 4355b9c547cSRui Paulo 4365b9c547cSRui Paulo /** 4375b9c547cSRui Paulo * ieee802_1x_kay_get_peer_sci 4385b9c547cSRui Paulo */ 4395b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 4405b9c547cSRui Paulo ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, 4415b9c547cSRui Paulo const struct ieee802_1x_mka_sci *sci) 4425b9c547cSRui Paulo { 4435b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 4445b9c547cSRui Paulo 4455b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 4465b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 447780fb4a2SCy Schubert if (sci_equal(&peer->sci, sci)) 4485b9c547cSRui Paulo return peer; 4495b9c547cSRui Paulo } 4505b9c547cSRui Paulo 4515b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers, 4525b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 453780fb4a2SCy Schubert if (sci_equal(&peer->sci, sci)) 4545b9c547cSRui Paulo return peer; 4555b9c547cSRui Paulo } 4565b9c547cSRui Paulo 4575b9c547cSRui Paulo return NULL; 4585b9c547cSRui Paulo } 4595b9c547cSRui Paulo 4605b9c547cSRui Paulo 46185732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey); 46285732ac8SCy Schubert 4635b9c547cSRui Paulo /** 4645b9c547cSRui Paulo * ieee802_1x_kay_init_receive_sa - 4655b9c547cSRui Paulo */ 4665b9c547cSRui Paulo static struct receive_sa * 4675b9c547cSRui Paulo ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, 4685b9c547cSRui Paulo struct data_key *key) 4695b9c547cSRui Paulo { 4705b9c547cSRui Paulo struct receive_sa *psa; 4715b9c547cSRui Paulo 4725b9c547cSRui Paulo if (!psc || !key) 4735b9c547cSRui Paulo return NULL; 4745b9c547cSRui Paulo 4755b9c547cSRui Paulo psa = os_zalloc(sizeof(*psa)); 4765b9c547cSRui Paulo if (!psa) { 4775b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 4785b9c547cSRui Paulo return NULL; 4795b9c547cSRui Paulo } 4805b9c547cSRui Paulo 48185732ac8SCy Schubert ieee802_1x_kay_use_data_key(key); 4825b9c547cSRui Paulo psa->pkey = key; 4835b9c547cSRui Paulo psa->lowest_pn = lowest_pn; 4845b9c547cSRui Paulo psa->next_pn = lowest_pn; 4855b9c547cSRui Paulo psa->an = an; 4865b9c547cSRui Paulo psa->sc = psc; 4875b9c547cSRui Paulo 4885b9c547cSRui Paulo os_get_time(&psa->created_time); 4895b9c547cSRui Paulo psa->in_use = FALSE; 4905b9c547cSRui Paulo 4915b9c547cSRui Paulo dl_list_add(&psc->sa_list, &psa->list); 4925b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 493*4bc52338SCy Schubert "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC", 49485732ac8SCy Schubert an, lowest_pn); 4955b9c547cSRui Paulo 4965b9c547cSRui Paulo return psa; 4975b9c547cSRui Paulo } 4985b9c547cSRui Paulo 4995b9c547cSRui Paulo 50085732ac8SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey); 50185732ac8SCy Schubert 5025b9c547cSRui Paulo /** 5035b9c547cSRui Paulo * ieee802_1x_kay_deinit_receive_sa - 5045b9c547cSRui Paulo */ 5055b9c547cSRui Paulo static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) 5065b9c547cSRui Paulo { 50785732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(psa->pkey); 5085b9c547cSRui Paulo psa->pkey = NULL; 5095b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 510780fb4a2SCy Schubert "KaY: Delete receive SA(an: %hhu) of SC", 511780fb4a2SCy Schubert psa->an); 5125b9c547cSRui Paulo dl_list_del(&psa->list); 5135b9c547cSRui Paulo os_free(psa); 5145b9c547cSRui Paulo } 5155b9c547cSRui Paulo 5165b9c547cSRui Paulo 5175b9c547cSRui Paulo /** 5185b9c547cSRui Paulo * ieee802_1x_kay_init_receive_sc - 5195b9c547cSRui Paulo */ 5205b9c547cSRui Paulo static struct receive_sc * 52185732ac8SCy Schubert ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci) 5225b9c547cSRui Paulo { 5235b9c547cSRui Paulo struct receive_sc *psc; 5245b9c547cSRui Paulo 5255b9c547cSRui Paulo if (!psci) 5265b9c547cSRui Paulo return NULL; 5275b9c547cSRui Paulo 5285b9c547cSRui Paulo psc = os_zalloc(sizeof(*psc)); 5295b9c547cSRui Paulo if (!psc) { 5305b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 5315b9c547cSRui Paulo return NULL; 5325b9c547cSRui Paulo } 5335b9c547cSRui Paulo 5345b9c547cSRui Paulo os_memcpy(&psc->sci, psci, sizeof(psc->sci)); 5355b9c547cSRui Paulo 5365b9c547cSRui Paulo os_get_time(&psc->created_time); 5375b9c547cSRui Paulo psc->receiving = FALSE; 5385b9c547cSRui Paulo 5395b9c547cSRui Paulo dl_list_init(&psc->sa_list); 540*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s", 541*4bc52338SCy Schubert sci_txt(&psc->sci)); 5425b9c547cSRui Paulo 5435b9c547cSRui Paulo return psc; 5445b9c547cSRui Paulo } 5455b9c547cSRui Paulo 5465b9c547cSRui Paulo 54785732ac8SCy Schubert static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay, 54885732ac8SCy Schubert struct receive_sa *sa) 54985732ac8SCy Schubert { 55085732ac8SCy Schubert secy_disable_receive_sa(kay, sa); 55185732ac8SCy Schubert secy_delete_receive_sa(kay, sa); 55285732ac8SCy Schubert ieee802_1x_kay_deinit_receive_sa(sa); 55385732ac8SCy Schubert } 55485732ac8SCy Schubert 55585732ac8SCy Schubert 5565b9c547cSRui Paulo /** 5575b9c547cSRui Paulo * ieee802_1x_kay_deinit_receive_sc - 5585b9c547cSRui Paulo **/ 5595b9c547cSRui Paulo static void 5605b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc( 5615b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, struct receive_sc *psc) 5625b9c547cSRui Paulo { 5635b9c547cSRui Paulo struct receive_sa *psa, *pre_sa; 5645b9c547cSRui Paulo 56585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Delete receive SC"); 5665b9c547cSRui Paulo dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa, 56785732ac8SCy Schubert list) 56885732ac8SCy Schubert ieee802_1x_delete_receive_sa(participant->kay, psa); 56985732ac8SCy Schubert 5705b9c547cSRui Paulo dl_list_del(&psc->list); 57185732ac8SCy Schubert secy_delete_receive_sc(participant->kay, psc); 5725b9c547cSRui Paulo os_free(psc); 5735b9c547cSRui Paulo } 5745b9c547cSRui Paulo 5755b9c547cSRui Paulo 576780fb4a2SCy Schubert static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer) 577780fb4a2SCy Schubert { 578*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s", 579*4bc52338SCy Schubert mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci)); 580780fb4a2SCy Schubert } 581780fb4a2SCy Schubert 582780fb4a2SCy Schubert 5835b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 584780fb4a2SCy Schubert ieee802_1x_kay_create_peer(const u8 *mi, u32 mn) 5855b9c547cSRui Paulo { 5865b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 5875b9c547cSRui Paulo 5885b9c547cSRui Paulo peer = os_zalloc(sizeof(*peer)); 589780fb4a2SCy Schubert if (!peer) { 5905b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); 5915b9c547cSRui Paulo return NULL; 5925b9c547cSRui Paulo } 5935b9c547cSRui Paulo 5945b9c547cSRui Paulo os_memcpy(peer->mi, mi, MI_LEN); 5955b9c547cSRui Paulo peer->mn = mn; 5965b9c547cSRui Paulo peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; 5975b9c547cSRui Paulo peer->sak_used = FALSE; 598*4bc52338SCy Schubert peer->missing_sak_use_count = 0; 599780fb4a2SCy Schubert 600780fb4a2SCy Schubert return peer; 601780fb4a2SCy Schubert } 602780fb4a2SCy Schubert 603780fb4a2SCy Schubert 604780fb4a2SCy Schubert /** 605780fb4a2SCy Schubert * ieee802_1x_kay_create_live_peer 606780fb4a2SCy Schubert */ 607780fb4a2SCy Schubert static struct ieee802_1x_kay_peer * 608780fb4a2SCy Schubert ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, 609780fb4a2SCy Schubert const u8 *mi, u32 mn) 610780fb4a2SCy Schubert { 611780fb4a2SCy Schubert struct ieee802_1x_kay_peer *peer; 612780fb4a2SCy Schubert struct receive_sc *rxsc; 613780fb4a2SCy Schubert 614780fb4a2SCy Schubert peer = ieee802_1x_kay_create_peer(mi, mn); 615780fb4a2SCy Schubert if (!peer) 616780fb4a2SCy Schubert return NULL; 617780fb4a2SCy Schubert 6185b9c547cSRui Paulo os_memcpy(&peer->sci, &participant->current_peer_sci, 6195b9c547cSRui Paulo sizeof(peer->sci)); 6205b9c547cSRui Paulo 62185732ac8SCy Schubert rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci); 622780fb4a2SCy Schubert if (!rxsc) { 623780fb4a2SCy Schubert os_free(peer); 6245b9c547cSRui Paulo return NULL; 625780fb4a2SCy Schubert } 6265b9c547cSRui Paulo 627*4bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) { 628*4bc52338SCy Schubert os_free(rxsc); 629*4bc52338SCy Schubert os_free(peer); 630*4bc52338SCy Schubert return NULL; 631*4bc52338SCy Schubert } 632780fb4a2SCy Schubert dl_list_add(&participant->live_peers, &peer->list); 6335b9c547cSRui Paulo dl_list_add(&participant->rxsc_list, &rxsc->list); 6345b9c547cSRui Paulo 6355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Live peer created"); 636780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer); 6375b9c547cSRui Paulo 6385b9c547cSRui Paulo return peer; 6395b9c547cSRui Paulo } 6405b9c547cSRui Paulo 6415b9c547cSRui Paulo 6425b9c547cSRui Paulo /** 6435b9c547cSRui Paulo * ieee802_1x_kay_create_potential_peer 6445b9c547cSRui Paulo */ 6455b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 6465b9c547cSRui Paulo ieee802_1x_kay_create_potential_peer( 6475b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn) 6485b9c547cSRui Paulo { 6495b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 6505b9c547cSRui Paulo 651780fb4a2SCy Schubert peer = ieee802_1x_kay_create_peer(mi, mn); 652780fb4a2SCy Schubert if (!peer) 6535b9c547cSRui Paulo return NULL; 6545b9c547cSRui Paulo 6555b9c547cSRui Paulo dl_list_add(&participant->potential_peers, &peer->list); 6565b9c547cSRui Paulo 657*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Potential peer created"); 658780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer); 6595b9c547cSRui Paulo 6605b9c547cSRui Paulo return peer; 6615b9c547cSRui Paulo } 6625b9c547cSRui Paulo 6635b9c547cSRui Paulo 6645b9c547cSRui Paulo /** 6655b9c547cSRui Paulo * ieee802_1x_kay_move_live_peer 6665b9c547cSRui Paulo */ 6675b9c547cSRui Paulo static struct ieee802_1x_kay_peer * 6685b9c547cSRui Paulo ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, 6695b9c547cSRui Paulo u8 *mi, u32 mn) 6705b9c547cSRui Paulo { 6715b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 6725b9c547cSRui Paulo struct receive_sc *rxsc; 6735b9c547cSRui Paulo 674780fb4a2SCy Schubert peer = ieee802_1x_kay_get_potential_peer(participant, mi); 67585732ac8SCy Schubert if (!peer) 67685732ac8SCy Schubert return NULL; 677780fb4a2SCy Schubert 67885732ac8SCy Schubert rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci); 679780fb4a2SCy Schubert if (!rxsc) 680780fb4a2SCy Schubert return NULL; 6815b9c547cSRui Paulo 6825b9c547cSRui Paulo os_memcpy(&peer->sci, &participant->current_peer_sci, 6835b9c547cSRui Paulo sizeof(peer->sci)); 6845b9c547cSRui Paulo peer->mn = mn; 6855b9c547cSRui Paulo peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; 6865b9c547cSRui Paulo 687*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer"); 688780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer); 6895b9c547cSRui Paulo 6905b9c547cSRui Paulo dl_list_del(&peer->list); 691*4bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) { 692*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer"); 693*4bc52338SCy Schubert os_free(rxsc); 694*4bc52338SCy Schubert os_free(peer); 695*4bc52338SCy Schubert return NULL; 696*4bc52338SCy Schubert } 6975b9c547cSRui Paulo dl_list_add_tail(&participant->live_peers, &peer->list); 6985b9c547cSRui Paulo 6995b9c547cSRui Paulo dl_list_add(&participant->rxsc_list, &rxsc->list); 7005b9c547cSRui Paulo 7015b9c547cSRui Paulo return peer; 7025b9c547cSRui Paulo } 7035b9c547cSRui Paulo 7045b9c547cSRui Paulo 7055b9c547cSRui Paulo 7065b9c547cSRui Paulo /** 7075b9c547cSRui Paulo * ieee802_1x_mka_basic_body_present - 7085b9c547cSRui Paulo */ 7095b9c547cSRui Paulo static Boolean 7105b9c547cSRui Paulo ieee802_1x_mka_basic_body_present( 7115b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 7125b9c547cSRui Paulo { 7135b9c547cSRui Paulo return TRUE; 7145b9c547cSRui Paulo } 7155b9c547cSRui Paulo 7165b9c547cSRui Paulo 7175b9c547cSRui Paulo /** 7185b9c547cSRui Paulo * ieee802_1x_mka_basic_body_length - 7195b9c547cSRui Paulo */ 7205b9c547cSRui Paulo static int 7215b9c547cSRui Paulo ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant) 7225b9c547cSRui Paulo { 7235b9c547cSRui Paulo int length; 7245b9c547cSRui Paulo 7255b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_basic_body); 7265b9c547cSRui Paulo length += participant->ckn.len; 727780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 7285b9c547cSRui Paulo } 7295b9c547cSRui Paulo 7305b9c547cSRui Paulo 7315b9c547cSRui Paulo /** 7325b9c547cSRui Paulo * ieee802_1x_mka_encode_basic_body 7335b9c547cSRui Paulo */ 7345b9c547cSRui Paulo static int 7355b9c547cSRui Paulo ieee802_1x_mka_encode_basic_body( 7365b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 7375b9c547cSRui Paulo struct wpabuf *buf) 7385b9c547cSRui Paulo { 7395b9c547cSRui Paulo struct ieee802_1x_mka_basic_body *body; 7405b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 741*4bc52338SCy Schubert unsigned int length = sizeof(struct ieee802_1x_mka_basic_body); 7425b9c547cSRui Paulo 743*4bc52338SCy Schubert length += participant->ckn.len; 744*4bc52338SCy Schubert body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length)); 7455b9c547cSRui Paulo 7465b9c547cSRui Paulo body->version = kay->mka_version; 7475b9c547cSRui Paulo body->priority = kay->actor_priority; 748*4bc52338SCy Schubert /* The Key Server flag is set if and only if the participant has not 749*4bc52338SCy Schubert * decided that another participant is or will be the Key Server. */ 7505b9c547cSRui Paulo if (participant->is_elected) 7515b9c547cSRui Paulo body->key_server = participant->is_key_server; 7525b9c547cSRui Paulo else 7535b9c547cSRui Paulo body->key_server = participant->can_be_key_server; 7545b9c547cSRui Paulo 7555b9c547cSRui Paulo body->macsec_desired = kay->macsec_desired; 756780fb4a2SCy Schubert body->macsec_capability = kay->macsec_capable; 7575b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 7585b9c547cSRui Paulo 7595b9c547cSRui Paulo os_memcpy(body->actor_sci.addr, kay->actor_sci.addr, 7605b9c547cSRui Paulo sizeof(kay->actor_sci.addr)); 761780fb4a2SCy Schubert body->actor_sci.port = kay->actor_sci.port; 7625b9c547cSRui Paulo 7635b9c547cSRui Paulo os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi)); 7645b9c547cSRui Paulo participant->mn = participant->mn + 1; 7655b9c547cSRui Paulo body->actor_mn = host_to_be32(participant->mn); 766780fb4a2SCy Schubert os_memcpy(body->algo_agility, kay->algo_agility, 7675b9c547cSRui Paulo sizeof(body->algo_agility)); 7685b9c547cSRui Paulo 7695b9c547cSRui Paulo os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len); 7705b9c547cSRui Paulo 7715b9c547cSRui Paulo ieee802_1x_mka_dump_basic_body(body); 7725b9c547cSRui Paulo 7735b9c547cSRui Paulo return 0; 7745b9c547cSRui Paulo } 7755b9c547cSRui Paulo 7765b9c547cSRui Paulo 777780fb4a2SCy Schubert static Boolean 778780fb4a2SCy Schubert reset_participant_mi(struct ieee802_1x_mka_participant *participant) 779780fb4a2SCy Schubert { 780780fb4a2SCy Schubert if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) 781780fb4a2SCy Schubert return FALSE; 782780fb4a2SCy Schubert participant->mn = 0; 783780fb4a2SCy Schubert 784780fb4a2SCy Schubert return TRUE; 785780fb4a2SCy Schubert } 786780fb4a2SCy Schubert 787780fb4a2SCy Schubert 7885b9c547cSRui Paulo /** 7895b9c547cSRui Paulo * ieee802_1x_mka_decode_basic_body - 7905b9c547cSRui Paulo */ 7915b9c547cSRui Paulo static struct ieee802_1x_mka_participant * 7925b9c547cSRui Paulo ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, 7935b9c547cSRui Paulo size_t msg_len) 7945b9c547cSRui Paulo { 7955b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 7965b9c547cSRui Paulo const struct ieee802_1x_mka_basic_body *body; 7975b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 79885732ac8SCy Schubert size_t ckn_len; 79985732ac8SCy Schubert size_t body_len; 8005b9c547cSRui Paulo 8015b9c547cSRui Paulo body = (const struct ieee802_1x_mka_basic_body *) mka_msg; 8025b9c547cSRui Paulo 8035b9c547cSRui Paulo if (body->version > MKA_VERSION_ID) { 8045b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 805*4bc52338SCy Schubert "KaY: Peer's version(%d) greater than MKA current version(%d)", 8065b9c547cSRui Paulo body->version, MKA_VERSION_ID); 8075b9c547cSRui Paulo } 8085b9c547cSRui Paulo if (kay->is_obliged_key_server && body->key_server) { 809*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server"); 8105b9c547cSRui Paulo return NULL; 8115b9c547cSRui Paulo } 8125b9c547cSRui Paulo 81385732ac8SCy Schubert body_len = get_mka_param_body_len(body); 81485732ac8SCy Schubert if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { 81585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", 81685732ac8SCy Schubert body_len); 81785732ac8SCy Schubert return NULL; 81885732ac8SCy Schubert } 81985732ac8SCy Schubert ckn_len = body_len - 82085732ac8SCy Schubert (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); 82185732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); 8225b9c547cSRui Paulo if (!participant) { 823*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 824*4bc52338SCy Schubert "KaY: Peer is not included in my CA - ignore MKPDU"); 8255b9c547cSRui Paulo return NULL; 8265b9c547cSRui Paulo } 8275b9c547cSRui Paulo 8285b9c547cSRui Paulo /* If the peer's MI is my MI, I will choose new MI */ 8295b9c547cSRui Paulo if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { 830780fb4a2SCy Schubert if (!reset_participant_mi(participant)) 8315b9c547cSRui Paulo return NULL; 832*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 833*4bc52338SCy Schubert "KaY: Peer using my MI - selected a new random MI: %s", 834*4bc52338SCy Schubert mi_txt(participant->mi)); 8355b9c547cSRui Paulo } 8365b9c547cSRui Paulo 8375b9c547cSRui Paulo os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); 838780fb4a2SCy Schubert participant->current_peer_id.mn = body->actor_mn; 8395b9c547cSRui Paulo os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr, 8405b9c547cSRui Paulo sizeof(participant->current_peer_sci.addr)); 841780fb4a2SCy Schubert participant->current_peer_sci.port = body->actor_sci.port; 8425b9c547cSRui Paulo 8435b9c547cSRui Paulo /* handler peer */ 8445b9c547cSRui Paulo peer = ieee802_1x_kay_get_peer(participant, body->actor_mi); 8455b9c547cSRui Paulo if (!peer) { 846*4bc52338SCy Schubert /* Check duplicated SCI 847*4bc52338SCy Schubert * 848*4bc52338SCy Schubert * A duplicated SCI indicates either an active attacker or 849*4bc52338SCy Schubert * a valid peer whose MI is being changed. The latter scenario 850*4bc52338SCy Schubert * is more likely because to have gotten this far the received 851*4bc52338SCy Schubert * MKPDU must have had a valid ICV, indicating the peer holds 852*4bc52338SCy Schubert * the same CAK as our participant. 853*4bc52338SCy Schubert * 854*4bc52338SCy Schubert * Before creating a new peer object for the new MI we must 855*4bc52338SCy Schubert * clean up the resources (SCs and SAs) associated with the 856*4bc52338SCy Schubert * old peer. An easy way to do this is to ignore MKPDUs with 857*4bc52338SCy Schubert * the new MI's for now and just wait for the old peer to 858*4bc52338SCy Schubert * time out and clean itself up (within MKA_LIFE_TIME). 859*4bc52338SCy Schubert * 860*4bc52338SCy Schubert * This method is preferable to deleting the old peer here 861*4bc52338SCy Schubert * and now and continuing on with processing because if this 862*4bc52338SCy Schubert * MKPDU is from an attacker it's better to ignore the MKPDU 863*4bc52338SCy Schubert * than to process it (and delete a valid peer as well). 8645b9c547cSRui Paulo */ 8655b9c547cSRui Paulo peer = ieee802_1x_kay_get_peer_sci(participant, 8665b9c547cSRui Paulo &body->actor_sci); 8675b9c547cSRui Paulo if (peer) { 868*4bc52338SCy Schubert time_t new_expire; 869*4bc52338SCy Schubert 8705b9c547cSRui Paulo wpa_printf(MSG_WARNING, 871*4bc52338SCy Schubert "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU"); 872*4bc52338SCy Schubert /* Reduce timeout to speed up this process but left the 873*4bc52338SCy Schubert * chance for old one to prove aliveness. */ 874*4bc52338SCy Schubert new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000; 875*4bc52338SCy Schubert if (peer->expire > new_expire) 876*4bc52338SCy Schubert peer->expire = new_expire; 877*4bc52338SCy Schubert return NULL; 8785b9c547cSRui Paulo } 8795b9c547cSRui Paulo 8805b9c547cSRui Paulo peer = ieee802_1x_kay_create_potential_peer( 8815b9c547cSRui Paulo participant, body->actor_mi, 8825b9c547cSRui Paulo be_to_host32(body->actor_mn)); 883*4bc52338SCy Schubert if (!peer) { 884*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 885*4bc52338SCy Schubert "KaY: No potential peer entry found - ignore MKPDU"); 8865b9c547cSRui Paulo return NULL; 887*4bc52338SCy Schubert } 8885b9c547cSRui Paulo 8895b9c547cSRui Paulo peer->macsec_desired = body->macsec_desired; 890780fb4a2SCy Schubert peer->macsec_capability = body->macsec_capability; 8915b9c547cSRui Paulo peer->is_key_server = (Boolean) body->key_server; 8925b9c547cSRui Paulo peer->key_server_priority = body->priority; 8935b9c547cSRui Paulo } else if (peer->mn < be_to_host32(body->actor_mn)) { 8945b9c547cSRui Paulo peer->mn = be_to_host32(body->actor_mn); 8955b9c547cSRui Paulo peer->macsec_desired = body->macsec_desired; 896780fb4a2SCy Schubert peer->macsec_capability = body->macsec_capability; 8975b9c547cSRui Paulo peer->is_key_server = (Boolean) body->key_server; 8985b9c547cSRui Paulo peer->key_server_priority = body->priority; 8995b9c547cSRui Paulo } else { 900*4bc52338SCy Schubert wpa_printf(MSG_WARNING, 901*4bc52338SCy Schubert "KaY: The peer MN did not increase - ignore MKPDU"); 9025b9c547cSRui Paulo return NULL; 9035b9c547cSRui Paulo } 9045b9c547cSRui Paulo 9055b9c547cSRui Paulo return participant; 9065b9c547cSRui Paulo } 9075b9c547cSRui Paulo 9085b9c547cSRui Paulo 9095b9c547cSRui Paulo /** 9105b9c547cSRui Paulo * ieee802_1x_mka_live_peer_body_present 9115b9c547cSRui Paulo */ 9125b9c547cSRui Paulo static Boolean 9135b9c547cSRui Paulo ieee802_1x_mka_live_peer_body_present( 9145b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 9155b9c547cSRui Paulo { 9165b9c547cSRui Paulo return !dl_list_empty(&participant->live_peers); 9175b9c547cSRui Paulo } 9185b9c547cSRui Paulo 9195b9c547cSRui Paulo 9205b9c547cSRui Paulo /** 9215b9c547cSRui Paulo * ieee802_1x_kay_get_live_peer_length 9225b9c547cSRui Paulo */ 9235b9c547cSRui Paulo static int 9245b9c547cSRui Paulo ieee802_1x_mka_get_live_peer_length( 9255b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 9265b9c547cSRui Paulo { 9275b9c547cSRui Paulo int len = MKA_HDR_LEN; 9285b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 9295b9c547cSRui Paulo 9305b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 9315b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 9325b9c547cSRui Paulo len += sizeof(struct ieee802_1x_mka_peer_id); 9335b9c547cSRui Paulo 934780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(len); 9355b9c547cSRui Paulo } 9365b9c547cSRui Paulo 9375b9c547cSRui Paulo 9385b9c547cSRui Paulo /** 9395b9c547cSRui Paulo * ieee802_1x_mka_encode_live_peer_body - 9405b9c547cSRui Paulo */ 9415b9c547cSRui Paulo static int 9425b9c547cSRui Paulo ieee802_1x_mka_encode_live_peer_body( 9435b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 9445b9c547cSRui Paulo struct wpabuf *buf) 9455b9c547cSRui Paulo { 9465b9c547cSRui Paulo struct ieee802_1x_mka_peer_body *body; 9475b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 9485b9c547cSRui Paulo unsigned int length; 9495b9c547cSRui Paulo struct ieee802_1x_mka_peer_id *body_peer; 9505b9c547cSRui Paulo 9515b9c547cSRui Paulo length = ieee802_1x_mka_get_live_peer_length(participant); 9525b9c547cSRui Paulo body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body)); 9535b9c547cSRui Paulo 9545b9c547cSRui Paulo body->type = MKA_LIVE_PEER_LIST; 9555b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 9565b9c547cSRui Paulo 9575b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 9585b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 9595b9c547cSRui Paulo body_peer = wpabuf_put(buf, 9605b9c547cSRui Paulo sizeof(struct ieee802_1x_mka_peer_id)); 9615b9c547cSRui Paulo os_memcpy(body_peer->mi, peer->mi, MI_LEN); 9625b9c547cSRui Paulo body_peer->mn = host_to_be32(peer->mn); 9635b9c547cSRui Paulo } 9645b9c547cSRui Paulo 9655b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(body); 9665b9c547cSRui Paulo return 0; 9675b9c547cSRui Paulo } 9685b9c547cSRui Paulo 9695b9c547cSRui Paulo /** 9705b9c547cSRui Paulo * ieee802_1x_mka_potential_peer_body_present 9715b9c547cSRui Paulo */ 9725b9c547cSRui Paulo static Boolean 9735b9c547cSRui Paulo ieee802_1x_mka_potential_peer_body_present( 9745b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 9755b9c547cSRui Paulo { 9765b9c547cSRui Paulo return !dl_list_empty(&participant->potential_peers); 9775b9c547cSRui Paulo } 9785b9c547cSRui Paulo 9795b9c547cSRui Paulo 9805b9c547cSRui Paulo /** 9815b9c547cSRui Paulo * ieee802_1x_kay_get_potential_peer_length 9825b9c547cSRui Paulo */ 9835b9c547cSRui Paulo static int 9845b9c547cSRui Paulo ieee802_1x_mka_get_potential_peer_length( 9855b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 9865b9c547cSRui Paulo { 9875b9c547cSRui Paulo int len = MKA_HDR_LEN; 9885b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 9895b9c547cSRui Paulo 9905b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers, 9915b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 9925b9c547cSRui Paulo len += sizeof(struct ieee802_1x_mka_peer_id); 9935b9c547cSRui Paulo 994780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(len); 9955b9c547cSRui Paulo } 9965b9c547cSRui Paulo 9975b9c547cSRui Paulo 9985b9c547cSRui Paulo /** 9995b9c547cSRui Paulo * ieee802_1x_mka_encode_potential_peer_body - 10005b9c547cSRui Paulo */ 10015b9c547cSRui Paulo static int 10025b9c547cSRui Paulo ieee802_1x_mka_encode_potential_peer_body( 10035b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 10045b9c547cSRui Paulo struct wpabuf *buf) 10055b9c547cSRui Paulo { 10065b9c547cSRui Paulo struct ieee802_1x_mka_peer_body *body; 10075b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 10085b9c547cSRui Paulo unsigned int length; 10095b9c547cSRui Paulo struct ieee802_1x_mka_peer_id *body_peer; 10105b9c547cSRui Paulo 10115b9c547cSRui Paulo length = ieee802_1x_mka_get_potential_peer_length(participant); 10125b9c547cSRui Paulo body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body)); 10135b9c547cSRui Paulo 10145b9c547cSRui Paulo body->type = MKA_POTENTIAL_PEER_LIST; 10155b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 10165b9c547cSRui Paulo 10175b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers, 10185b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 10195b9c547cSRui Paulo body_peer = wpabuf_put(buf, 10205b9c547cSRui Paulo sizeof(struct ieee802_1x_mka_peer_id)); 10215b9c547cSRui Paulo os_memcpy(body_peer->mi, peer->mi, MI_LEN); 10225b9c547cSRui Paulo body_peer->mn = host_to_be32(peer->mn); 10235b9c547cSRui Paulo } 10245b9c547cSRui Paulo 10255b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(body); 10265b9c547cSRui Paulo return 0; 10275b9c547cSRui Paulo } 10285b9c547cSRui Paulo 10295b9c547cSRui Paulo 10305b9c547cSRui Paulo /** 10315b9c547cSRui Paulo * ieee802_1x_mka_i_in_peerlist - 10325b9c547cSRui Paulo */ 10335b9c547cSRui Paulo static Boolean 10345b9c547cSRui Paulo ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, 10355b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 10365b9c547cSRui Paulo { 10375b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 10385b9c547cSRui Paulo size_t body_len; 10395b9c547cSRui Paulo size_t left_len; 1040780fb4a2SCy Schubert u8 body_type; 10415b9c547cSRui Paulo const u8 *pos; 10425b9c547cSRui Paulo size_t i; 10435b9c547cSRui Paulo 1044780fb4a2SCy Schubert for (pos = mka_msg, left_len = msg_len; 1045780fb4a2SCy Schubert left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; 1046*4bc52338SCy Schubert left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN, 1047*4bc52338SCy Schubert pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) { 10485b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos; 10495b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 10505b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr); 10515b9c547cSRui Paulo 105285732ac8SCy Schubert if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) { 10535b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1054780fb4a2SCy Schubert "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", 1055780fb4a2SCy Schubert left_len, MKA_HDR_LEN, 105685732ac8SCy Schubert MKA_ALIGN_LENGTH(body_len), 105785732ac8SCy Schubert DEFAULT_ICV_LEN); 105885732ac8SCy Schubert return FALSE; 10595b9c547cSRui Paulo } 10605b9c547cSRui Paulo 106185732ac8SCy Schubert if (body_type != MKA_LIVE_PEER_LIST && 106285732ac8SCy Schubert body_type != MKA_POTENTIAL_PEER_LIST) 106385732ac8SCy Schubert continue; 106485732ac8SCy Schubert 10655b9c547cSRui Paulo if ((body_len % 16) != 0) { 10665b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1067780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", 1068780fb4a2SCy Schubert body_len); 1069780fb4a2SCy Schubert continue; 10705b9c547cSRui Paulo } 10715b9c547cSRui Paulo 107285732ac8SCy Schubert ieee802_1x_mka_dump_peer_body( 107385732ac8SCy Schubert (struct ieee802_1x_mka_peer_body *)pos); 107485732ac8SCy Schubert 1075780fb4a2SCy Schubert for (i = 0; i < body_len; 1076780fb4a2SCy Schubert i += sizeof(struct ieee802_1x_mka_peer_id)) { 1077780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi; 10785b9c547cSRui Paulo 1079780fb4a2SCy Schubert peer_mi = (const struct ieee802_1x_mka_peer_id *) 1080780fb4a2SCy Schubert (pos + MKA_HDR_LEN + i); 1081780fb4a2SCy Schubert if (os_memcmp(peer_mi->mi, participant->mi, 1082*4bc52338SCy Schubert MI_LEN) == 0) { 1083*4bc52338SCy Schubert u32 mn = be_to_host32(peer_mi->mn); 1084*4bc52338SCy Schubert 1085*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 1086*4bc52338SCy Schubert "KaY: My MI - received MN %u, most recently transmitted MN %u", 1087*4bc52338SCy Schubert mn, participant->mn); 1088*4bc52338SCy Schubert if (mn == participant->mn) 10895b9c547cSRui Paulo return TRUE; 1090780fb4a2SCy Schubert } 10915b9c547cSRui Paulo } 1092*4bc52338SCy Schubert } 10935b9c547cSRui Paulo 10945b9c547cSRui Paulo return FALSE; 10955b9c547cSRui Paulo } 10965b9c547cSRui Paulo 10975b9c547cSRui Paulo 10985b9c547cSRui Paulo /** 10995b9c547cSRui Paulo * ieee802_1x_mka_decode_live_peer_body - 11005b9c547cSRui Paulo */ 11015b9c547cSRui Paulo static int ieee802_1x_mka_decode_live_peer_body( 11025b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 11035b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len) 11045b9c547cSRui Paulo { 11055b9c547cSRui Paulo const struct ieee802_1x_mka_hdr *hdr; 11065b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 11075b9c547cSRui Paulo size_t body_len; 11085b9c547cSRui Paulo size_t i; 11095b9c547cSRui Paulo Boolean is_included; 11105b9c547cSRui Paulo 11115b9c547cSRui Paulo is_included = ieee802_1x_kay_is_in_live_peer( 11125b9c547cSRui Paulo participant, participant->current_peer_id.mi); 11135b9c547cSRui Paulo 11145b9c547cSRui Paulo hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; 11155b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 1116780fb4a2SCy Schubert if (body_len % 16 != 0) { 1117780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 1118780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", 1119780fb4a2SCy Schubert body_len); 1120780fb4a2SCy Schubert return -1; 1121780fb4a2SCy Schubert } 11225b9c547cSRui Paulo 1123780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { 1124780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi; 1125780fb4a2SCy Schubert u32 peer_mn; 1126780fb4a2SCy Schubert 1127780fb4a2SCy Schubert peer_mi = (const struct ieee802_1x_mka_peer_id *) 1128780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i); 1129780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn); 11305b9c547cSRui Paulo 11315b9c547cSRui Paulo /* it is myself */ 11325b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { 11335b9c547cSRui Paulo /* My message id is used by other participant */ 1134780fb4a2SCy Schubert if (peer_mn > participant->mn && 1135780fb4a2SCy Schubert !reset_participant_mi(participant)) 1136780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 11375b9c547cSRui Paulo continue; 11385b9c547cSRui Paulo } 1139780fb4a2SCy Schubert 11405b9c547cSRui Paulo if (!is_included) 11415b9c547cSRui Paulo continue; 11425b9c547cSRui Paulo 1143780fb4a2SCy Schubert peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi); 1144780fb4a2SCy Schubert if (peer) { 11455b9c547cSRui Paulo peer->mn = peer_mn; 1146780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_potential_peer( 1147780fb4a2SCy Schubert participant, peer_mi->mi, peer_mn)) { 11485b9c547cSRui Paulo return -1; 11495b9c547cSRui Paulo } 11505b9c547cSRui Paulo } 11515b9c547cSRui Paulo 11525b9c547cSRui Paulo return 0; 11535b9c547cSRui Paulo } 11545b9c547cSRui Paulo 11555b9c547cSRui Paulo 11565b9c547cSRui Paulo /** 11575b9c547cSRui Paulo * ieee802_1x_mka_decode_potential_peer_body - 11585b9c547cSRui Paulo */ 11595b9c547cSRui Paulo static int 11605b9c547cSRui Paulo ieee802_1x_mka_decode_potential_peer_body( 11615b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 11625b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len) 11635b9c547cSRui Paulo { 1164780fb4a2SCy Schubert const struct ieee802_1x_mka_hdr *hdr; 11655b9c547cSRui Paulo size_t body_len; 11665b9c547cSRui Paulo size_t i; 11675b9c547cSRui Paulo 1168780fb4a2SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; 11695b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 1170780fb4a2SCy Schubert if (body_len % 16 != 0) { 1171780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 1172780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", 1173780fb4a2SCy Schubert body_len); 1174780fb4a2SCy Schubert return -1; 1175780fb4a2SCy Schubert } 11765b9c547cSRui Paulo 1177780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { 1178780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi; 1179780fb4a2SCy Schubert u32 peer_mn; 1180780fb4a2SCy Schubert 1181780fb4a2SCy Schubert peer_mi = (struct ieee802_1x_mka_peer_id *) 1182780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i); 1183780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn); 11845b9c547cSRui Paulo 11855b9c547cSRui Paulo /* it is myself */ 11865b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { 11875b9c547cSRui Paulo /* My message id is used by other participant */ 1188780fb4a2SCy Schubert if (peer_mn > participant->mn && 1189780fb4a2SCy Schubert !reset_participant_mi(participant)) 1190780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 11915b9c547cSRui Paulo continue; 11925b9c547cSRui Paulo } 11935b9c547cSRui Paulo } 11945b9c547cSRui Paulo 11955b9c547cSRui Paulo return 0; 11965b9c547cSRui Paulo } 11975b9c547cSRui Paulo 11985b9c547cSRui Paulo 11995b9c547cSRui Paulo /** 12005b9c547cSRui Paulo * ieee802_1x_mka_sak_use_body_present 12015b9c547cSRui Paulo */ 12025b9c547cSRui Paulo static Boolean 12035b9c547cSRui Paulo ieee802_1x_mka_sak_use_body_present( 12045b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 12055b9c547cSRui Paulo { 1206780fb4a2SCy Schubert return participant->to_use_sak; 12075b9c547cSRui Paulo } 12085b9c547cSRui Paulo 12095b9c547cSRui Paulo 12105b9c547cSRui Paulo /** 12115b9c547cSRui Paulo * ieee802_1x_mka_get_sak_use_length 12125b9c547cSRui Paulo */ 12135b9c547cSRui Paulo static int 12145b9c547cSRui Paulo ieee802_1x_mka_get_sak_use_length( 12155b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 12165b9c547cSRui Paulo { 12175b9c547cSRui Paulo int length = MKA_HDR_LEN; 12185b9c547cSRui Paulo 12195b9c547cSRui Paulo if (participant->kay->macsec_desired && participant->advised_desired) 12205b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_sak_use_body); 12215b9c547cSRui Paulo 1222780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 12235b9c547cSRui Paulo } 12245b9c547cSRui Paulo 12255b9c547cSRui Paulo 12265b9c547cSRui Paulo /** 1227*4bc52338SCy Schubert * ieee802_1x_mka_get_lpn 12285b9c547cSRui Paulo */ 12295b9c547cSRui Paulo static u32 12305b9c547cSRui Paulo ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal, 12315b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki) 12325b9c547cSRui Paulo { 1233*4bc52338SCy Schubert struct transmit_sa *txsa; 12345b9c547cSRui Paulo u32 lpn = 0; 12355b9c547cSRui Paulo 1236*4bc52338SCy Schubert dl_list_for_each(txsa, &principal->txsc->sa_list, 1237*4bc52338SCy Schubert struct transmit_sa, list) { 1238*4bc52338SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { 1239*4bc52338SCy Schubert /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses 1240*4bc52338SCy Schubert * MKA to communicate the lowest PN used for 1241*4bc52338SCy Schubert * transmission with the SAK within the last two 1242*4bc52338SCy Schubert * seconds". Achieve this 2 second delay by setting the 1243*4bc52338SCy Schubert * lpn using the transmit next PN (i.e., txsa->next_pn) 1244*4bc52338SCy Schubert * that was read last time here (i.e., mka_hello_time 1245*4bc52338SCy Schubert * 2 seconds ago). 1246*4bc52338SCy Schubert * 1247*4bc52338SCy Schubert * The lowest acceptable PN is the same as the last 1248*4bc52338SCy Schubert * transmitted PN, which is one less than the next 1249*4bc52338SCy Schubert * transmit PN. 1250*4bc52338SCy Schubert * 1251*4bc52338SCy Schubert * NOTE: This method only works if mka_hello_time is 2s. 1252*4bc52338SCy Schubert */ 1253*4bc52338SCy Schubert lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0; 12545b9c547cSRui Paulo 1255*4bc52338SCy Schubert /* Now read the current transmit next PN for use next 1256*4bc52338SCy Schubert * time through. */ 1257*4bc52338SCy Schubert secy_get_transmit_next_pn(principal->kay, txsa); 12585b9c547cSRui Paulo break; 12595b9c547cSRui Paulo } 12605b9c547cSRui Paulo } 12615b9c547cSRui Paulo 12625b9c547cSRui Paulo if (lpn == 0) 12635b9c547cSRui Paulo lpn = 1; 12645b9c547cSRui Paulo 12655b9c547cSRui Paulo return lpn; 12665b9c547cSRui Paulo } 12675b9c547cSRui Paulo 12685b9c547cSRui Paulo 12695b9c547cSRui Paulo /** 12705b9c547cSRui Paulo * ieee802_1x_mka_encode_sak_use_body - 12715b9c547cSRui Paulo */ 12725b9c547cSRui Paulo static int 12735b9c547cSRui Paulo ieee802_1x_mka_encode_sak_use_body( 12745b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 12755b9c547cSRui Paulo struct wpabuf *buf) 12765b9c547cSRui Paulo { 12775b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body; 1278780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 12795b9c547cSRui Paulo unsigned int length; 12805b9c547cSRui Paulo u32 pn = 1; 12815b9c547cSRui Paulo 12825b9c547cSRui Paulo length = ieee802_1x_mka_get_sak_use_length(participant); 1283780fb4a2SCy Schubert body = wpabuf_put(buf, length); 12845b9c547cSRui Paulo 12855b9c547cSRui Paulo body->type = MKA_SAK_USE; 12865b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 12875b9c547cSRui Paulo 12885b9c547cSRui Paulo if (length == MKA_HDR_LEN) { 12895b9c547cSRui Paulo body->ptx = TRUE; 12905b9c547cSRui Paulo body->prx = TRUE; 12915b9c547cSRui Paulo body->lan = 0; 12925b9c547cSRui Paulo body->lrx = FALSE; 12935b9c547cSRui Paulo body->ltx = FALSE; 12945b9c547cSRui Paulo body->delay_protect = FALSE; 12955b9c547cSRui Paulo return 0; 12965b9c547cSRui Paulo } 12975b9c547cSRui Paulo 1298*4bc52338SCy Schubert /* data delay protect */ 1299*4bc52338SCy Schubert body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME; 1300*4bc52338SCy Schubert /* lowest accept packet number */ 13015b9c547cSRui Paulo pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); 1302780fb4a2SCy Schubert if (pn > kay->pn_exhaustion) { 13035b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); 13045b9c547cSRui Paulo if (participant->is_key_server) 13055b9c547cSRui Paulo participant->new_sak = TRUE; 13065b9c547cSRui Paulo } 13075b9c547cSRui Paulo 13085b9c547cSRui Paulo body->llpn = host_to_be32(pn); 13095b9c547cSRui Paulo pn = ieee802_1x_mka_get_lpn(participant, &participant->oki); 13105b9c547cSRui Paulo body->olpn = host_to_be32(pn); 13115b9c547cSRui Paulo 13125b9c547cSRui Paulo /* plain tx, plain rx */ 1313780fb4a2SCy Schubert body->ptx = !kay->macsec_protect; 1314780fb4a2SCy Schubert body->prx = kay->macsec_validate != Strict; 13155b9c547cSRui Paulo 13165b9c547cSRui Paulo /* latest key: rx, tx, key server member identifier key number */ 13175b9c547cSRui Paulo body->lan = participant->lan; 1318780fb4a2SCy Schubert os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi)); 13195b9c547cSRui Paulo body->lkn = host_to_be32(participant->lki.kn); 13205b9c547cSRui Paulo body->lrx = participant->lrx; 13215b9c547cSRui Paulo body->ltx = participant->ltx; 13225b9c547cSRui Paulo 13235b9c547cSRui Paulo /* old key: rx, tx, key server member identifier key number */ 13245b9c547cSRui Paulo body->oan = participant->oan; 13255b9c547cSRui Paulo if (participant->oki.kn != participant->lki.kn && 13265b9c547cSRui Paulo participant->oki.kn != 0) { 13275b9c547cSRui Paulo body->otx = TRUE; 13285b9c547cSRui Paulo body->orx = TRUE; 13295b9c547cSRui Paulo os_memcpy(body->osrv_mi, participant->oki.mi, 13305b9c547cSRui Paulo sizeof(body->osrv_mi)); 13315b9c547cSRui Paulo body->okn = host_to_be32(participant->oki.kn); 13325b9c547cSRui Paulo } else { 13335b9c547cSRui Paulo body->otx = FALSE; 13345b9c547cSRui Paulo body->orx = FALSE; 13355b9c547cSRui Paulo } 13365b9c547cSRui Paulo 13375b9c547cSRui Paulo /* set CP's variable */ 13385b9c547cSRui Paulo if (body->ltx) { 1339780fb4a2SCy Schubert kay->tx_enable = TRUE; 1340780fb4a2SCy Schubert kay->port_enable = TRUE; 13415b9c547cSRui Paulo } 1342780fb4a2SCy Schubert if (body->lrx) 1343780fb4a2SCy Schubert kay->rx_enable = TRUE; 13445b9c547cSRui Paulo 13455b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body); 13465b9c547cSRui Paulo return 0; 13475b9c547cSRui Paulo } 13485b9c547cSRui Paulo 13495b9c547cSRui Paulo 13505b9c547cSRui Paulo /** 13515b9c547cSRui Paulo * ieee802_1x_mka_decode_sak_use_body - 13525b9c547cSRui Paulo */ 13535b9c547cSRui Paulo static int 13545b9c547cSRui Paulo ieee802_1x_mka_decode_sak_use_body( 13555b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 13565b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 13575b9c547cSRui Paulo { 13585b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 13595b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body; 13605b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 1361*4bc52338SCy Schubert struct receive_sc *rxsc; 1362*4bc52338SCy Schubert struct receive_sa *rxsa; 13635b9c547cSRui Paulo struct data_key *sa_key = NULL; 13645b9c547cSRui Paulo size_t body_len; 13655b9c547cSRui Paulo struct ieee802_1x_mka_ki ki; 13665b9c547cSRui Paulo u32 lpn; 13675b9c547cSRui Paulo Boolean all_receiving; 1368780fb4a2SCy Schubert Boolean found; 1369780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 13705b9c547cSRui Paulo 13715b9c547cSRui Paulo if (!participant->principal) { 13725b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); 13735b9c547cSRui Paulo return -1; 13745b9c547cSRui Paulo } 13755b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant, 13765b9c547cSRui Paulo participant->current_peer_id.mi); 13775b9c547cSRui Paulo if (!peer) { 1378*4bc52338SCy Schubert wpa_printf(MSG_WARNING, 1379*4bc52338SCy Schubert "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set", 1380*4bc52338SCy Schubert mi_txt(participant->current_peer_id.mi)); 13815b9c547cSRui Paulo return -1; 13825b9c547cSRui Paulo } 13835b9c547cSRui Paulo 13845b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 13855b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 13865b9c547cSRui Paulo body = (struct ieee802_1x_mka_sak_use_body *) mka_msg; 13875b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body); 13885b9c547cSRui Paulo 13895b9c547cSRui Paulo if ((body_len != 0) && (body_len < 40)) { 13905b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1391780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets", 1392780fb4a2SCy Schubert body_len); 13935b9c547cSRui Paulo return -1; 13945b9c547cSRui Paulo } 13955b9c547cSRui Paulo 13965b9c547cSRui Paulo /* TODO: what action should I take when peer does not support MACsec */ 13975b9c547cSRui Paulo if (body_len == 0) { 13985b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec"); 13995b9c547cSRui Paulo return 0; 14005b9c547cSRui Paulo } 14015b9c547cSRui Paulo 14025b9c547cSRui Paulo /* TODO: when the plain tx or rx of peer is true, should I change 14035b9c547cSRui Paulo * the attribute of controlled port 14045b9c547cSRui Paulo */ 14055b9c547cSRui Paulo if (body->prx) 14065b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE"); 14075b9c547cSRui Paulo 14085b9c547cSRui Paulo if (body->ptx) 14095b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE"); 14105b9c547cSRui Paulo 14115b9c547cSRui Paulo /* check latest key is valid */ 14125b9c547cSRui Paulo if (body->ltx || body->lrx) { 1413780fb4a2SCy Schubert found = FALSE; 14145b9c547cSRui Paulo os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); 1415780fb4a2SCy Schubert ki.kn = be_to_host32(body->lkn); 14165b9c547cSRui Paulo dl_list_for_each(sa_key, &participant->sak_list, 14175b9c547cSRui Paulo struct data_key, list) { 14185b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, &ki)) { 1419780fb4a2SCy Schubert found = TRUE; 14205b9c547cSRui Paulo break; 14215b9c547cSRui Paulo } 14225b9c547cSRui Paulo } 1423780fb4a2SCy Schubert if (!found) { 1424*4bc52338SCy Schubert wpa_printf(MSG_INFO, "KaY: Latest key is invalid"); 14255b9c547cSRui Paulo return -1; 14265b9c547cSRui Paulo } 14275b9c547cSRui Paulo if (os_memcmp(participant->lki.mi, body->lsrv_mi, 14285b9c547cSRui Paulo sizeof(participant->lki.mi)) == 0 && 1429780fb4a2SCy Schubert be_to_host32(body->lkn) == participant->lki.kn && 14305b9c547cSRui Paulo body->lan == participant->lan) { 14315b9c547cSRui Paulo peer->sak_used = TRUE; 14325b9c547cSRui Paulo } 14335b9c547cSRui Paulo if (body->ltx && peer->is_key_server) { 1434780fb4a2SCy Schubert ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); 1435780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 14365b9c547cSRui Paulo } 14375b9c547cSRui Paulo } 14385b9c547cSRui Paulo 143985732ac8SCy Schubert /* check old key is valid (but only if we remember our old key) */ 144085732ac8SCy Schubert if (participant->oki.kn != 0 && (body->otx || body->orx)) { 14415b9c547cSRui Paulo if (os_memcmp(participant->oki.mi, body->osrv_mi, 14425b9c547cSRui Paulo sizeof(participant->oki.mi)) != 0 || 1443780fb4a2SCy Schubert be_to_host32(body->okn) != participant->oki.kn || 14445b9c547cSRui Paulo body->oan != participant->oan) { 14455b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); 14465b9c547cSRui Paulo return -1; 14475b9c547cSRui Paulo } 14485b9c547cSRui Paulo } 14495b9c547cSRui Paulo 14505b9c547cSRui Paulo /* TODO: how to set the MACsec hardware when delay_protect is true */ 1451780fb4a2SCy Schubert if (body->delay_protect && 1452780fb4a2SCy Schubert (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { 14535b9c547cSRui Paulo wpa_printf(MSG_WARNING, 1454*4bc52338SCy Schubert "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE"); 14555b9c547cSRui Paulo return -1; 14565b9c547cSRui Paulo } 14575b9c547cSRui Paulo 14585b9c547cSRui Paulo /* check all live peer have used the sak for receiving sa */ 14595b9c547cSRui Paulo all_receiving = TRUE; 14605b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 14615b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 14625b9c547cSRui Paulo if (!peer->sak_used) { 14635b9c547cSRui Paulo all_receiving = FALSE; 14645b9c547cSRui Paulo break; 14655b9c547cSRui Paulo } 14665b9c547cSRui Paulo } 14675b9c547cSRui Paulo if (all_receiving) { 14685b9c547cSRui Paulo participant->to_dist_sak = FALSE; 1469780fb4a2SCy Schubert ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); 1470780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 14715b9c547cSRui Paulo } 14725b9c547cSRui Paulo 1473*4bc52338SCy Schubert /* if I'm key server, and detects peer member pn exhaustion, rekey. */ 1474780fb4a2SCy Schubert lpn = be_to_host32(body->llpn); 1475780fb4a2SCy Schubert if (lpn > kay->pn_exhaustion) { 14765b9c547cSRui Paulo if (participant->is_key_server) { 14775b9c547cSRui Paulo participant->new_sak = TRUE; 14785b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); 14795b9c547cSRui Paulo } 14805b9c547cSRui Paulo } 14815b9c547cSRui Paulo 1482*4bc52338SCy Schubert if (sa_key) 1483*4bc52338SCy Schubert sa_key->next_pn = lpn; 1484780fb4a2SCy Schubert found = FALSE; 1485*4bc52338SCy Schubert dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc, 1486*4bc52338SCy Schubert list) { 1487*4bc52338SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, 1488*4bc52338SCy Schubert list) { 1489*4bc52338SCy Schubert if (sa_key && rxsa->pkey == sa_key) { 1490780fb4a2SCy Schubert found = TRUE; 14915b9c547cSRui Paulo break; 14925b9c547cSRui Paulo } 14935b9c547cSRui Paulo } 1494*4bc52338SCy Schubert if (found) 1495*4bc52338SCy Schubert break; 1496*4bc52338SCy Schubert } 1497780fb4a2SCy Schubert if (!found) { 1498*4bc52338SCy Schubert wpa_printf(MSG_WARNING, "KaY: Can't find rxsa"); 14995b9c547cSRui Paulo return -1; 15005b9c547cSRui Paulo } 15015b9c547cSRui Paulo 1502*4bc52338SCy Schubert if (body->delay_protect) { 1503*4bc52338SCy Schubert secy_get_receive_lowest_pn(participant->kay, rxsa); 1504*4bc52338SCy Schubert if (lpn > rxsa->lowest_pn) { 1505*4bc52338SCy Schubert /* Delay protect window (communicated via MKA) is 1506*4bc52338SCy Schubert * tighter than SecY's current replay protect window, 1507*4bc52338SCy Schubert * so tell SecY the new (and higher) lpn. */ 1508*4bc52338SCy Schubert rxsa->lowest_pn = lpn; 1509*4bc52338SCy Schubert secy_set_receive_lowest_pn(participant->kay, rxsa); 1510*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn); 1511*4bc52338SCy Schubert } 1512*4bc52338SCy Schubert /* FIX: Delay protection for olpn not implemented. 1513*4bc52338SCy Schubert * Note that Old Key is only active for MKA_SAK_RETIRE_TIME 1514*4bc52338SCy Schubert * (3 seconds) and delay protection does allow PN's within 1515*4bc52338SCy Schubert * a 2 seconds window, so olpn would be a lot of work for 1516*4bc52338SCy Schubert * just 1 second's worth of protection. */ 15175b9c547cSRui Paulo } 15185b9c547cSRui Paulo 15195b9c547cSRui Paulo return 0; 15205b9c547cSRui Paulo } 15215b9c547cSRui Paulo 15225b9c547cSRui Paulo 15235b9c547cSRui Paulo /** 15245b9c547cSRui Paulo * ieee802_1x_mka_dist_sak_body_present 15255b9c547cSRui Paulo */ 15265b9c547cSRui Paulo static Boolean 15275b9c547cSRui Paulo ieee802_1x_mka_dist_sak_body_present( 15285b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 15295b9c547cSRui Paulo { 1530*4bc52338SCy Schubert return participant->is_key_server && participant->to_dist_sak && 1531*4bc52338SCy Schubert participant->new_key; 15325b9c547cSRui Paulo } 15335b9c547cSRui Paulo 15345b9c547cSRui Paulo 15355b9c547cSRui Paulo /** 15365b9c547cSRui Paulo * ieee802_1x_kay_get_dist_sak_length 15375b9c547cSRui Paulo */ 15385b9c547cSRui Paulo static int 15395b9c547cSRui Paulo ieee802_1x_mka_get_dist_sak_length( 15405b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 15415b9c547cSRui Paulo { 1542780fb4a2SCy Schubert int length = MKA_HDR_LEN; 1543780fb4a2SCy Schubert unsigned int cs_index = participant->kay->macsec_csindex; 15445b9c547cSRui Paulo 1545780fb4a2SCy Schubert if (participant->advised_desired && cs_index < CS_TABLE_SIZE) { 15465b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_dist_sak_body); 15475b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX) 15485b9c547cSRui Paulo length += CS_ID_LEN; 15495b9c547cSRui Paulo 15505b9c547cSRui Paulo length += cipher_suite_tbl[cs_index].sak_len + 8; 15515b9c547cSRui Paulo } 15525b9c547cSRui Paulo 1553780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 15545b9c547cSRui Paulo } 15555b9c547cSRui Paulo 15565b9c547cSRui Paulo 15575b9c547cSRui Paulo /** 15585b9c547cSRui Paulo * ieee802_1x_mka_encode_dist_sak_body - 15595b9c547cSRui Paulo */ 15605b9c547cSRui Paulo static int 15615b9c547cSRui Paulo ieee802_1x_mka_encode_dist_sak_body( 15625b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 15635b9c547cSRui Paulo struct wpabuf *buf) 15645b9c547cSRui Paulo { 15655b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body; 15665b9c547cSRui Paulo struct data_key *sak; 15675b9c547cSRui Paulo unsigned int length; 1568780fb4a2SCy Schubert unsigned int cs_index; 15695b9c547cSRui Paulo int sak_pos; 15705b9c547cSRui Paulo 15715b9c547cSRui Paulo length = ieee802_1x_mka_get_dist_sak_length(participant); 15725b9c547cSRui Paulo body = wpabuf_put(buf, length); 15735b9c547cSRui Paulo body->type = MKA_DISTRIBUTED_SAK; 15745b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 15755b9c547cSRui Paulo if (length == MKA_HDR_LEN) { 15765b9c547cSRui Paulo body->confid_offset = 0; 15775b9c547cSRui Paulo body->dan = 0; 15785b9c547cSRui Paulo return 0; 15795b9c547cSRui Paulo } 15805b9c547cSRui Paulo 15815b9c547cSRui Paulo sak = participant->new_key; 1582*4bc52338SCy Schubert if (!sak) { 1583*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 1584*4bc52338SCy Schubert "KaY: No SAK available to build Distributed SAK parameter set"); 1585*4bc52338SCy Schubert return -1; 1586*4bc52338SCy Schubert } 15875b9c547cSRui Paulo body->confid_offset = sak->confidentiality_offset; 15885b9c547cSRui Paulo body->dan = sak->an; 15895b9c547cSRui Paulo body->kn = host_to_be32(sak->key_identifier.kn); 15905b9c547cSRui Paulo cs_index = participant->kay->macsec_csindex; 15915b9c547cSRui Paulo sak_pos = 0; 1592780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE) 1593780fb4a2SCy Schubert return -1; 15945b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX) { 1595780fb4a2SCy Schubert be64 cs; 1596780fb4a2SCy Schubert 1597780fb4a2SCy Schubert cs = host_to_be64(cipher_suite_tbl[cs_index].id); 1598780fb4a2SCy Schubert os_memcpy(body->sak, &cs, CS_ID_LEN); 15995b9c547cSRui Paulo sak_pos = CS_ID_LEN; 16005b9c547cSRui Paulo } 1601*4bc52338SCy Schubert if (aes_wrap(participant->kek.key, participant->kek.len, 16025b9c547cSRui Paulo cipher_suite_tbl[cs_index].sak_len / 8, 16035b9c547cSRui Paulo sak->key, body->sak + sak_pos)) { 16045b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); 16055b9c547cSRui Paulo return -1; 16065b9c547cSRui Paulo } 16075b9c547cSRui Paulo 16085b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body); 16095b9c547cSRui Paulo 16105b9c547cSRui Paulo return 0; 16115b9c547cSRui Paulo } 16125b9c547cSRui Paulo 16135b9c547cSRui Paulo 16145b9c547cSRui Paulo /** 16155b9c547cSRui Paulo * ieee802_1x_kay_init_data_key - 16165b9c547cSRui Paulo */ 1617780fb4a2SCy Schubert static void ieee802_1x_kay_init_data_key(struct data_key *pkey) 16185b9c547cSRui Paulo { 1619780fb4a2SCy Schubert pkey->transmits = TRUE; 1620780fb4a2SCy Schubert pkey->receives = TRUE; 16215b9c547cSRui Paulo os_get_time(&pkey->created_time); 16225b9c547cSRui Paulo 1623*4bc52338SCy Schubert pkey->next_pn = 1; 16245b9c547cSRui Paulo pkey->user = 1; 16255b9c547cSRui Paulo } 16265b9c547cSRui Paulo 16275b9c547cSRui Paulo 16285b9c547cSRui Paulo /** 16295b9c547cSRui Paulo * ieee802_1x_kay_decode_dist_sak_body - 16305b9c547cSRui Paulo */ 16315b9c547cSRui Paulo static int 16325b9c547cSRui Paulo ieee802_1x_mka_decode_dist_sak_body( 16335b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 16345b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 16355b9c547cSRui Paulo { 16365b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 16375b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body; 16385b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 16395b9c547cSRui Paulo struct macsec_ciphersuite *cs; 16405b9c547cSRui Paulo size_t body_len; 16415b9c547cSRui Paulo struct data_key *sa_key = NULL; 16425b9c547cSRui Paulo int sak_len; 16435b9c547cSRui Paulo u8 *wrap_sak; 16445b9c547cSRui Paulo u8 *unwrap_sak; 1645780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 16465b9c547cSRui Paulo 16475b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 16485b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 16495b9c547cSRui Paulo if ((body_len != 0) && (body_len != 28) && (body_len < 36)) { 16505b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1651780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets", 1652780fb4a2SCy Schubert body_len); 16535b9c547cSRui Paulo return -1; 16545b9c547cSRui Paulo } 16555b9c547cSRui Paulo 16565b9c547cSRui Paulo if (!participant->principal) { 16575b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16585b9c547cSRui Paulo "KaY: I can't accept the distributed SAK as I am not principal"); 16595b9c547cSRui Paulo return -1; 16605b9c547cSRui Paulo } 16615b9c547cSRui Paulo if (participant->is_key_server) { 16625b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1663*4bc52338SCy Schubert "KaY: Reject distributed SAK since I'm a key server"); 16645b9c547cSRui Paulo return -1; 16655b9c547cSRui Paulo } 1666780fb4a2SCy Schubert if (!kay->macsec_desired || 1667780fb4a2SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 16685b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16695b9c547cSRui Paulo "KaY: I am not MACsec-desired or without MACsec capable"); 16705b9c547cSRui Paulo return -1; 16715b9c547cSRui Paulo } 16725b9c547cSRui Paulo 16735b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant, 16745b9c547cSRui Paulo participant->current_peer_id.mi); 16755b9c547cSRui Paulo if (!peer) { 16765b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16775b9c547cSRui Paulo "KaY: The key server is not in my live peers list"); 16785b9c547cSRui Paulo return -1; 16795b9c547cSRui Paulo } 1680780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &peer->sci)) { 16815b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: The key server is not elected"); 16825b9c547cSRui Paulo return -1; 16835b9c547cSRui Paulo } 1684780fb4a2SCy Schubert 16855b9c547cSRui Paulo if (body_len == 0) { 1686780fb4a2SCy Schubert kay->authenticated = TRUE; 1687780fb4a2SCy Schubert kay->secured = FALSE; 1688780fb4a2SCy Schubert kay->failed = FALSE; 16895b9c547cSRui Paulo participant->advised_desired = FALSE; 1690780fb4a2SCy Schubert ieee802_1x_cp_connect_authenticated(kay->cp); 1691780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 16925b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec"); 169385732ac8SCy Schubert participant->to_use_sak = FALSE; 16945b9c547cSRui Paulo return 0; 16955b9c547cSRui Paulo } 1696780fb4a2SCy Schubert 16975b9c547cSRui Paulo participant->advised_desired = TRUE; 1698780fb4a2SCy Schubert kay->authenticated = FALSE; 1699780fb4a2SCy Schubert kay->secured = TRUE; 1700780fb4a2SCy Schubert kay->failed = FALSE; 1701780fb4a2SCy Schubert ieee802_1x_cp_connect_secure(kay->cp); 1702780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 17035b9c547cSRui Paulo 17045b9c547cSRui Paulo body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg; 17055b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body); 17065b9c547cSRui Paulo dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list) 17075b9c547cSRui Paulo { 17085b9c547cSRui Paulo if (os_memcmp(sa_key->key_identifier.mi, 17095b9c547cSRui Paulo participant->current_peer_id.mi, MI_LEN) == 0 && 17105b9c547cSRui Paulo sa_key->key_identifier.kn == be_to_host32(body->kn)) { 1711*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 1712*4bc52338SCy Schubert "KaY: SAK has already been installed - do not set it again"); 17135b9c547cSRui Paulo return 0; 17145b9c547cSRui Paulo } 17155b9c547cSRui Paulo } 1716780fb4a2SCy Schubert 17175b9c547cSRui Paulo if (body_len == 28) { 17185b9c547cSRui Paulo sak_len = DEFAULT_SA_KEY_LEN; 17195b9c547cSRui Paulo wrap_sak = body->sak; 1720780fb4a2SCy Schubert kay->macsec_csindex = DEFAULT_CS_INDEX; 1721780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex]; 17225b9c547cSRui Paulo } else { 1723*4bc52338SCy Schubert unsigned int idx; 1724*4bc52338SCy Schubert 1725*4bc52338SCy Schubert cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak, 1726*4bc52338SCy Schubert &idx); 17275b9c547cSRui Paulo if (!cs) { 17285b9c547cSRui Paulo wpa_printf(MSG_ERROR, 17295b9c547cSRui Paulo "KaY: I can't support the Cipher Suite advised by key server"); 17305b9c547cSRui Paulo return -1; 17315b9c547cSRui Paulo } 17325b9c547cSRui Paulo sak_len = cs->sak_len; 17335b9c547cSRui Paulo wrap_sak = body->sak + CS_ID_LEN; 1734*4bc52338SCy Schubert kay->macsec_csindex = idx; 17355b9c547cSRui Paulo } 17365b9c547cSRui Paulo 17375b9c547cSRui Paulo unwrap_sak = os_zalloc(sak_len); 17385b9c547cSRui Paulo if (!unwrap_sak) { 17395b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 17405b9c547cSRui Paulo return -1; 17415b9c547cSRui Paulo } 1742*4bc52338SCy Schubert if (aes_unwrap(participant->kek.key, participant->kek.len, 1743*4bc52338SCy Schubert sak_len >> 3, wrap_sak, unwrap_sak)) { 17445b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); 17455b9c547cSRui Paulo os_free(unwrap_sak); 17465b9c547cSRui Paulo return -1; 17475b9c547cSRui Paulo } 1748*4bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:", 174985732ac8SCy Schubert unwrap_sak, sak_len); 17505b9c547cSRui Paulo 1751780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key)); 17525b9c547cSRui Paulo if (!sa_key) { 17535b9c547cSRui Paulo os_free(unwrap_sak); 17545b9c547cSRui Paulo return -1; 17555b9c547cSRui Paulo } 17565b9c547cSRui Paulo 1757780fb4a2SCy Schubert os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi, 1758780fb4a2SCy Schubert MI_LEN); 1759780fb4a2SCy Schubert sa_key->key_identifier.kn = be_to_host32(body->kn); 1760780fb4a2SCy Schubert 1761780fb4a2SCy Schubert sa_key->key = unwrap_sak; 1762780fb4a2SCy Schubert sa_key->key_len = sak_len; 1763780fb4a2SCy Schubert 1764780fb4a2SCy Schubert sa_key->confidentiality_offset = body->confid_offset; 1765780fb4a2SCy Schubert sa_key->an = body->dan; 1766780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key); 1767780fb4a2SCy Schubert 176885732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key); 17695b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list); 17705b9c547cSRui Paulo 1771780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); 1772780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 1773780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, body->confid_offset); 1774780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 1775780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); 1776780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, body->dan); 1777780fb4a2SCy Schubert ieee802_1x_cp_signal_newsak(kay->cp); 1778780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 17795b9c547cSRui Paulo 178085732ac8SCy Schubert kay->rcvd_keys++; 17815b9c547cSRui Paulo participant->to_use_sak = TRUE; 17825b9c547cSRui Paulo 17835b9c547cSRui Paulo return 0; 17845b9c547cSRui Paulo } 17855b9c547cSRui Paulo 17865b9c547cSRui Paulo 17875b9c547cSRui Paulo /** 17885b9c547cSRui Paulo * ieee802_1x_mka_icv_body_present 17895b9c547cSRui Paulo */ 17905b9c547cSRui Paulo static Boolean 17915b9c547cSRui Paulo ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant) 17925b9c547cSRui Paulo { 17935b9c547cSRui Paulo return TRUE; 17945b9c547cSRui Paulo } 17955b9c547cSRui Paulo 17965b9c547cSRui Paulo 17975b9c547cSRui Paulo /** 17985b9c547cSRui Paulo * ieee802_1x_kay_get_icv_length 17995b9c547cSRui Paulo */ 18005b9c547cSRui Paulo static int 18015b9c547cSRui Paulo ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) 18025b9c547cSRui Paulo { 18035b9c547cSRui Paulo int length; 18045b9c547cSRui Paulo 1805*4bc52338SCy Schubert /* Determine if we need space for the ICV Indicator */ 1806*4bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != 1807*4bc52338SCy Schubert DEFAULT_ICV_LEN) 18085b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_icv_body); 1809*4bc52338SCy Schubert else 1810*4bc52338SCy Schubert length = 0; 18115b9c547cSRui Paulo length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; 18125b9c547cSRui Paulo 1813780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 18145b9c547cSRui Paulo } 18155b9c547cSRui Paulo 18165b9c547cSRui Paulo 18175b9c547cSRui Paulo /** 18185b9c547cSRui Paulo * ieee802_1x_mka_encode_icv_body - 18195b9c547cSRui Paulo */ 18205b9c547cSRui Paulo static int 18215b9c547cSRui Paulo ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, 18225b9c547cSRui Paulo struct wpabuf *buf) 18235b9c547cSRui Paulo { 18245b9c547cSRui Paulo struct ieee802_1x_mka_icv_body *body; 18255b9c547cSRui Paulo unsigned int length; 18265b9c547cSRui Paulo u8 cmac[MAX_ICV_LEN]; 18275b9c547cSRui Paulo 18285b9c547cSRui Paulo length = ieee802_1x_mka_get_icv_length(participant); 1829*4bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != 1830*4bc52338SCy Schubert DEFAULT_ICV_LEN) { 1831*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: ICV Indicator"); 18325b9c547cSRui Paulo body = wpabuf_put(buf, MKA_HDR_LEN); 18335b9c547cSRui Paulo body->type = MKA_ICV_INDICATOR; 1834*4bc52338SCy Schubert length -= MKA_HDR_LEN; 1835*4bc52338SCy Schubert set_mka_param_body_len(body, length); 18365b9c547cSRui Paulo } 18375b9c547cSRui Paulo 18385b9c547cSRui Paulo if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash( 1839*4bc52338SCy Schubert participant->ick.key, participant->ick.len, 1840*4bc52338SCy Schubert wpabuf_head(buf), wpabuf_len(buf), cmac)) { 1841*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV"); 18425b9c547cSRui Paulo return -1; 18435b9c547cSRui Paulo } 1844*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length); 18455b9c547cSRui Paulo 18465b9c547cSRui Paulo os_memcpy(wpabuf_put(buf, length), cmac, length); 18475b9c547cSRui Paulo 18485b9c547cSRui Paulo return 0; 18495b9c547cSRui Paulo } 18505b9c547cSRui Paulo 18515b9c547cSRui Paulo /** 18525b9c547cSRui Paulo * ieee802_1x_mka_decode_icv_body - 18535b9c547cSRui Paulo */ 1854*4bc52338SCy Schubert static const u8 * 18555b9c547cSRui Paulo ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, 18565b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 18575b9c547cSRui Paulo { 1858*4bc52338SCy Schubert const struct ieee802_1x_mka_hdr *hdr; 1859*4bc52338SCy Schubert const struct ieee802_1x_mka_icv_body *body; 18605b9c547cSRui Paulo size_t body_len; 18615b9c547cSRui Paulo size_t left_len; 1862780fb4a2SCy Schubert u8 body_type; 18635b9c547cSRui Paulo const u8 *pos; 18645b9c547cSRui Paulo 18655b9c547cSRui Paulo pos = mka_msg; 18665b9c547cSRui Paulo left_len = msg_len; 1867*4bc52338SCy Schubert while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { 1868*4bc52338SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) pos; 1869*4bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 18705b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr); 18715b9c547cSRui Paulo 1872*4bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN) 18735b9c547cSRui Paulo break; 18745b9c547cSRui Paulo 18755b9c547cSRui Paulo if (body_type != MKA_ICV_INDICATOR) { 18765b9c547cSRui Paulo left_len -= MKA_HDR_LEN + body_len; 18775b9c547cSRui Paulo pos += MKA_HDR_LEN + body_len; 18785b9c547cSRui Paulo continue; 18795b9c547cSRui Paulo } 18805b9c547cSRui Paulo 1881*4bc52338SCy Schubert body = (const struct ieee802_1x_mka_icv_body *) pos; 18825b9c547cSRui Paulo if (body_len 1883*4bc52338SCy Schubert < mka_alg_tbl[participant->kay->mka_algindex].icv_len) 18845b9c547cSRui Paulo return NULL; 18855b9c547cSRui Paulo 18865b9c547cSRui Paulo return body->icv; 18875b9c547cSRui Paulo } 18885b9c547cSRui Paulo 1889*4bc52338SCy Schubert return mka_msg + msg_len - DEFAULT_ICV_LEN; 18905b9c547cSRui Paulo } 18915b9c547cSRui Paulo 18925b9c547cSRui Paulo 18935b9c547cSRui Paulo /** 18945b9c547cSRui Paulo * ieee802_1x_mka_decode_dist_cak_body- 18955b9c547cSRui Paulo */ 18965b9c547cSRui Paulo static int 18975b9c547cSRui Paulo ieee802_1x_mka_decode_dist_cak_body( 18985b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 18995b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19005b9c547cSRui Paulo { 19015b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 19025b9c547cSRui Paulo size_t body_len; 19035b9c547cSRui Paulo 19045b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 19055b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 19065b9c547cSRui Paulo if (body_len < 28) { 19075b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1908*4bc52338SCy Schubert "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets", 1909780fb4a2SCy Schubert body_len); 19105b9c547cSRui Paulo return -1; 19115b9c547cSRui Paulo } 19125b9c547cSRui Paulo 19135b9c547cSRui Paulo return 0; 19145b9c547cSRui Paulo } 19155b9c547cSRui Paulo 19165b9c547cSRui Paulo 19175b9c547cSRui Paulo /** 19185b9c547cSRui Paulo * ieee802_1x_mka_decode_kmd_body - 19195b9c547cSRui Paulo */ 19205b9c547cSRui Paulo static int 19215b9c547cSRui Paulo ieee802_1x_mka_decode_kmd_body( 19225b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 19235b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19245b9c547cSRui Paulo { 19255b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 19265b9c547cSRui Paulo size_t body_len; 19275b9c547cSRui Paulo 19285b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 19295b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 19305b9c547cSRui Paulo if (body_len < 5) { 19315b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1932*4bc52338SCy Schubert "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets", 1933780fb4a2SCy Schubert body_len); 19345b9c547cSRui Paulo return -1; 19355b9c547cSRui Paulo } 19365b9c547cSRui Paulo 19375b9c547cSRui Paulo return 0; 19385b9c547cSRui Paulo } 19395b9c547cSRui Paulo 19405b9c547cSRui Paulo 19415b9c547cSRui Paulo /** 19425b9c547cSRui Paulo * ieee802_1x_mka_decode_announce_body - 19435b9c547cSRui Paulo */ 19445b9c547cSRui Paulo static int ieee802_1x_mka_decode_announce_body( 19455b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 19465b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19475b9c547cSRui Paulo { 19485b9c547cSRui Paulo return 0; 19495b9c547cSRui Paulo } 19505b9c547cSRui Paulo 19515b9c547cSRui Paulo 1952780fb4a2SCy Schubert struct mka_param_body_handler { 1953780fb4a2SCy Schubert int (*body_tx)(struct ieee802_1x_mka_participant *participant, 1954780fb4a2SCy Schubert struct wpabuf *buf); 1955780fb4a2SCy Schubert int (*body_rx)(struct ieee802_1x_mka_participant *participant, 1956780fb4a2SCy Schubert const u8 *mka_msg, size_t msg_len); 1957780fb4a2SCy Schubert int (*body_length)(struct ieee802_1x_mka_participant *participant); 1958780fb4a2SCy Schubert Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); 1959780fb4a2SCy Schubert }; 1960780fb4a2SCy Schubert 1961780fb4a2SCy Schubert 1962780fb4a2SCy Schubert static struct mka_param_body_handler mka_body_handler[] = { 1963*4bc52338SCy Schubert /* Basic parameter set */ 19645b9c547cSRui Paulo { 1965780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_basic_body, 1966780fb4a2SCy Schubert .body_rx = NULL, 1967780fb4a2SCy Schubert .body_length = ieee802_1x_mka_basic_body_length, 1968780fb4a2SCy Schubert .body_present = ieee802_1x_mka_basic_body_present 19695b9c547cSRui Paulo }, 19705b9c547cSRui Paulo 1971*4bc52338SCy Schubert /* Live Peer List parameter set */ 19725b9c547cSRui Paulo { 1973780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_live_peer_body, 1974780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_live_peer_body, 1975780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_live_peer_length, 1976780fb4a2SCy Schubert .body_present = ieee802_1x_mka_live_peer_body_present 19775b9c547cSRui Paulo }, 19785b9c547cSRui Paulo 1979*4bc52338SCy Schubert /* Potential Peer List parameter set */ 19805b9c547cSRui Paulo { 1981780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_potential_peer_body, 1982780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_potential_peer_body, 1983780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_potential_peer_length, 1984780fb4a2SCy Schubert .body_present = ieee802_1x_mka_potential_peer_body_present 19855b9c547cSRui Paulo }, 19865b9c547cSRui Paulo 1987*4bc52338SCy Schubert /* MACsec SAK Use parameter set */ 19885b9c547cSRui Paulo { 1989780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_sak_use_body, 1990780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_sak_use_body, 1991780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_sak_use_length, 1992780fb4a2SCy Schubert .body_present = ieee802_1x_mka_sak_use_body_present 19935b9c547cSRui Paulo }, 19945b9c547cSRui Paulo 1995*4bc52338SCy Schubert /* Distributed SAK parameter set */ 19965b9c547cSRui Paulo { 1997780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_dist_sak_body, 1998780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_sak_body, 1999780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_dist_sak_length, 2000780fb4a2SCy Schubert .body_present = ieee802_1x_mka_dist_sak_body_present 20015b9c547cSRui Paulo }, 20025b9c547cSRui Paulo 2003*4bc52338SCy Schubert /* Distribute CAK parameter set */ 20045b9c547cSRui Paulo { 2005780fb4a2SCy Schubert .body_tx = NULL, 2006780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_cak_body, 2007780fb4a2SCy Schubert .body_length = NULL, 2008780fb4a2SCy Schubert .body_present = NULL 20095b9c547cSRui Paulo }, 20105b9c547cSRui Paulo 2011*4bc52338SCy Schubert /* KMD parameter set */ 20125b9c547cSRui Paulo { 2013780fb4a2SCy Schubert .body_tx = NULL, 2014780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_kmd_body, 2015780fb4a2SCy Schubert .body_length = NULL, 2016780fb4a2SCy Schubert .body_present = NULL 20175b9c547cSRui Paulo }, 20185b9c547cSRui Paulo 2019*4bc52338SCy Schubert /* Announcement parameter set */ 20205b9c547cSRui Paulo { 2021780fb4a2SCy Schubert .body_tx = NULL, 2022780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_announce_body, 2023780fb4a2SCy Schubert .body_length = NULL, 2024780fb4a2SCy Schubert .body_present = NULL 20255b9c547cSRui Paulo }, 20265b9c547cSRui Paulo 2027*4bc52338SCy Schubert /* ICV Indicator parameter set */ 20285b9c547cSRui Paulo { 2029780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_icv_body, 2030780fb4a2SCy Schubert .body_rx = NULL, 2031780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_icv_length, 2032780fb4a2SCy Schubert .body_present = ieee802_1x_mka_icv_body_present 20335b9c547cSRui Paulo }, 20345b9c547cSRui Paulo }; 20355b9c547cSRui Paulo 20365b9c547cSRui Paulo 20375b9c547cSRui Paulo /** 203885732ac8SCy Schubert * ieee802_1x_kay_use_data_key - Take reference on a key 203985732ac8SCy Schubert */ 204085732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey) 204185732ac8SCy Schubert { 204285732ac8SCy Schubert pkey->user++; 204385732ac8SCy Schubert } 204485732ac8SCy Schubert 204585732ac8SCy Schubert 204685732ac8SCy Schubert /** 204785732ac8SCy Schubert * ieee802_1x_kay_deinit_data_key - Release reference on a key and 204885732ac8SCy Schubert * free if there are no remaining users 20495b9c547cSRui Paulo */ 2050780fb4a2SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) 20515b9c547cSRui Paulo { 20525b9c547cSRui Paulo if (!pkey) 20535b9c547cSRui Paulo return; 20545b9c547cSRui Paulo 20555b9c547cSRui Paulo pkey->user--; 20565b9c547cSRui Paulo if (pkey->user > 1) 20575b9c547cSRui Paulo return; 20585b9c547cSRui Paulo 20595b9c547cSRui Paulo os_free(pkey->key); 20605b9c547cSRui Paulo os_free(pkey); 20615b9c547cSRui Paulo } 20625b9c547cSRui Paulo 20635b9c547cSRui Paulo 20645b9c547cSRui Paulo /** 20655b9c547cSRui Paulo * ieee802_1x_kay_generate_new_sak - 20665b9c547cSRui Paulo */ 20675b9c547cSRui Paulo static int 20685b9c547cSRui Paulo ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) 20695b9c547cSRui Paulo { 20705b9c547cSRui Paulo struct data_key *sa_key = NULL; 20715b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 20725b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 20735b9c547cSRui Paulo int ctx_len, ctx_offset; 20745b9c547cSRui Paulo u8 *context; 2075780fb4a2SCy Schubert unsigned int key_len; 2076780fb4a2SCy Schubert u8 *key; 2077780fb4a2SCy Schubert struct macsec_ciphersuite *cs; 20785b9c547cSRui Paulo 20795b9c547cSRui Paulo /* check condition for generating a fresh SAK: 20805b9c547cSRui Paulo * must have one live peer 20815b9c547cSRui Paulo * and MKA life time elapse since last distribution 20825b9c547cSRui Paulo * or potential peer is empty 20835b9c547cSRui Paulo */ 20845b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 20855b9c547cSRui Paulo wpa_printf(MSG_ERROR, 2086*4bc52338SCy Schubert "KaY: Live peers list must not be empty when generating fresh SAK"); 20875b9c547cSRui Paulo return -1; 20885b9c547cSRui Paulo } 20895b9c547cSRui Paulo 20905b9c547cSRui Paulo /* FIXME: A fresh SAK not generated until 20915b9c547cSRui Paulo * the live peer list contains at least one peer and 20925b9c547cSRui Paulo * MKA life time has elapsed since the prior SAK was first distributed, 20935b9c547cSRui Paulo * or the Key server's potential peer is empty 20945b9c547cSRui Paulo * but I can't understand the second item, so 20955b9c547cSRui Paulo * here only check first item and ingore 20965b9c547cSRui Paulo * && (!dl_list_empty(&participant->potential_peers))) { 20975b9c547cSRui Paulo */ 20985b9c547cSRui Paulo if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) { 20995b9c547cSRui Paulo wpa_printf(MSG_ERROR, 2100*4bc52338SCy Schubert "KaY: Life time has not elapsed since prior SAK distributed"); 21015b9c547cSRui Paulo return -1; 21025b9c547cSRui Paulo } 21035b9c547cSRui Paulo 2104780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex]; 2105780fb4a2SCy Schubert key_len = cs->sak_len; 2106780fb4a2SCy Schubert key = os_zalloc(key_len); 2107780fb4a2SCy Schubert if (!key) { 21085b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 21095b9c547cSRui Paulo return -1; 21105b9c547cSRui Paulo } 21115b9c547cSRui Paulo 2112780fb4a2SCy Schubert ctx_len = key_len + sizeof(kay->dist_kn); 21135b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21145b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 21155b9c547cSRui Paulo ctx_len += sizeof(peer->mi); 21165b9c547cSRui Paulo ctx_len += sizeof(participant->mi); 21175b9c547cSRui Paulo 21185b9c547cSRui Paulo context = os_zalloc(ctx_len); 2119780fb4a2SCy Schubert if (!context) 2120780fb4a2SCy Schubert goto fail; 2121780fb4a2SCy Schubert 21225b9c547cSRui Paulo ctx_offset = 0; 2123780fb4a2SCy Schubert if (os_get_random(context + ctx_offset, key_len) < 0) 2124780fb4a2SCy Schubert goto fail; 2125780fb4a2SCy Schubert 2126780fb4a2SCy Schubert ctx_offset += key_len; 21275b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21285b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 21295b9c547cSRui Paulo os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi)); 21305b9c547cSRui Paulo ctx_offset += sizeof(peer->mi); 21315b9c547cSRui Paulo } 21325b9c547cSRui Paulo os_memcpy(context + ctx_offset, participant->mi, 21335b9c547cSRui Paulo sizeof(participant->mi)); 21345b9c547cSRui Paulo ctx_offset += sizeof(participant->mi); 21355b9c547cSRui Paulo os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); 21365b9c547cSRui Paulo 2137*4bc52338SCy Schubert if (key_len == 16 || key_len == 32) { 2138*4bc52338SCy Schubert if (ieee802_1x_sak_aes_cmac(participant->cak.key, 2139*4bc52338SCy Schubert participant->cak.len, 2140*4bc52338SCy Schubert context, ctx_len, 2141*4bc52338SCy Schubert key, key_len)) { 2142*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK"); 2143*4bc52338SCy Schubert goto fail; 2144*4bc52338SCy Schubert } 21455b9c547cSRui Paulo } else { 2146*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported", 2147*4bc52338SCy Schubert key_len); 2148780fb4a2SCy Schubert goto fail; 21495b9c547cSRui Paulo } 215085732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); 2151780fb4a2SCy Schubert os_free(context); 2152780fb4a2SCy Schubert context = NULL; 21535b9c547cSRui Paulo 2154780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key)); 21555b9c547cSRui Paulo if (!sa_key) { 2156780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 2157780fb4a2SCy Schubert goto fail; 21585b9c547cSRui Paulo } 2159780fb4a2SCy Schubert 2160780fb4a2SCy Schubert sa_key->key = key; 2161780fb4a2SCy Schubert sa_key->key_len = key_len; 2162780fb4a2SCy Schubert os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN); 2163780fb4a2SCy Schubert sa_key->key_identifier.kn = kay->dist_kn; 2164780fb4a2SCy Schubert 2165780fb4a2SCy Schubert sa_key->confidentiality_offset = kay->macsec_confidentiality; 2166780fb4a2SCy Schubert sa_key->an = kay->dist_an; 2167780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key); 2168780fb4a2SCy Schubert 21695b9c547cSRui Paulo participant->new_key = sa_key; 21705b9c547cSRui Paulo 217185732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key); 21725b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list); 217385732ac8SCy Schubert 2174780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); 21755b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 2176780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); 21775b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 2178780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); 2179780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an); 21805b9c547cSRui Paulo ieee802_1x_cp_signal_newsak(kay->cp); 21815b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 21825b9c547cSRui Paulo 21835b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21845b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 21855b9c547cSRui Paulo peer->sak_used = FALSE; 21865b9c547cSRui Paulo 2187780fb4a2SCy Schubert kay->dist_kn++; 2188780fb4a2SCy Schubert kay->dist_an++; 2189780fb4a2SCy Schubert if (kay->dist_an > 3) 2190780fb4a2SCy Schubert kay->dist_an = 0; 21915b9c547cSRui Paulo 2192780fb4a2SCy Schubert kay->dist_time = time(NULL); 21935b9c547cSRui Paulo 21945b9c547cSRui Paulo return 0; 2195780fb4a2SCy Schubert 2196780fb4a2SCy Schubert fail: 2197780fb4a2SCy Schubert os_free(key); 2198780fb4a2SCy Schubert os_free(context); 2199780fb4a2SCy Schubert return -1; 2200780fb4a2SCy Schubert } 2201780fb4a2SCy Schubert 2202780fb4a2SCy Schubert 2203780fb4a2SCy Schubert static int compare_priorities(const struct ieee802_1x_kay_peer *peer, 2204780fb4a2SCy Schubert const struct ieee802_1x_kay_peer *other) 2205780fb4a2SCy Schubert { 2206780fb4a2SCy Schubert if (peer->key_server_priority < other->key_server_priority) 2207780fb4a2SCy Schubert return -1; 2208780fb4a2SCy Schubert if (other->key_server_priority < peer->key_server_priority) 2209780fb4a2SCy Schubert return 1; 2210780fb4a2SCy Schubert 2211780fb4a2SCy Schubert return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN); 22125b9c547cSRui Paulo } 22135b9c547cSRui Paulo 22145b9c547cSRui Paulo 22155b9c547cSRui Paulo /** 22165b9c547cSRui Paulo * ieee802_1x_kay_elect_key_server - elect the key server 22175b9c547cSRui Paulo * when to elect: whenever the live peers list changes 22185b9c547cSRui Paulo */ 22195b9c547cSRui Paulo static int 22205b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) 22215b9c547cSRui Paulo { 22225b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 22235b9c547cSRui Paulo struct ieee802_1x_kay_peer *key_server = NULL; 22245b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 22255b9c547cSRui Paulo Boolean i_is_key_server; 222685732ac8SCy Schubert int priority_comparison; 22275b9c547cSRui Paulo 22285b9c547cSRui Paulo if (participant->is_obliged_key_server) { 22295b9c547cSRui Paulo participant->new_sak = TRUE; 22305b9c547cSRui Paulo participant->to_dist_sak = FALSE; 22315b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, TRUE); 22325b9c547cSRui Paulo return 0; 22335b9c547cSRui Paulo } 22345b9c547cSRui Paulo 22355b9c547cSRui Paulo /* elect the key server among the peers */ 22365b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 22375b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 22385b9c547cSRui Paulo if (!peer->is_key_server) 22395b9c547cSRui Paulo continue; 22405b9c547cSRui Paulo 22415b9c547cSRui Paulo if (!key_server) { 22425b9c547cSRui Paulo key_server = peer; 22435b9c547cSRui Paulo continue; 22445b9c547cSRui Paulo } 22455b9c547cSRui Paulo 2246780fb4a2SCy Schubert if (compare_priorities(peer, key_server) < 0) 22475b9c547cSRui Paulo key_server = peer; 22485b9c547cSRui Paulo } 22495b9c547cSRui Paulo 22505b9c547cSRui Paulo /* elect the key server between me and the above elected peer */ 22515b9c547cSRui Paulo i_is_key_server = FALSE; 22525b9c547cSRui Paulo if (key_server && participant->can_be_key_server) { 2253780fb4a2SCy Schubert struct ieee802_1x_kay_peer tmp; 22545b9c547cSRui Paulo 2255780fb4a2SCy Schubert tmp.key_server_priority = kay->actor_priority; 2256780fb4a2SCy Schubert os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); 225785732ac8SCy Schubert priority_comparison = compare_priorities(&tmp, key_server); 225885732ac8SCy Schubert if (priority_comparison < 0) { 2259780fb4a2SCy Schubert i_is_key_server = TRUE; 226085732ac8SCy Schubert } else if (priority_comparison == 0) { 226185732ac8SCy Schubert wpa_printf(MSG_WARNING, 226285732ac8SCy Schubert "KaY: Cannot elect key server between me and peer, duplicate MAC detected"); 226385732ac8SCy Schubert key_server = NULL; 226485732ac8SCy Schubert } 2265780fb4a2SCy Schubert } else if (participant->can_be_key_server) { 2266780fb4a2SCy Schubert i_is_key_server = TRUE; 22675b9c547cSRui Paulo } 22685b9c547cSRui Paulo 22695b9c547cSRui Paulo if (i_is_key_server) { 22705b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, TRUE); 2271780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) { 22725b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp); 22735b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 22745b9c547cSRui Paulo } 22755b9c547cSRui Paulo 22765b9c547cSRui Paulo participant->is_key_server = TRUE; 22775b9c547cSRui Paulo participant->principal = TRUE; 22785b9c547cSRui Paulo participant->new_sak = TRUE; 2279*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: I am elected as key server"); 22805b9c547cSRui Paulo participant->to_dist_sak = FALSE; 22815b9c547cSRui Paulo participant->is_elected = TRUE; 22825b9c547cSRui Paulo 22835b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci, 22845b9c547cSRui Paulo sizeof(kay->key_server_sci)); 22855b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority; 2286780fb4a2SCy Schubert } else if (key_server) { 2287*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 2288*4bc52338SCy Schubert "KaY: Peer %s was elected as the key server", 2289*4bc52338SCy Schubert mi_txt(key_server->mi)); 22905b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, FALSE); 2291780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { 22925b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp); 22935b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 22945b9c547cSRui Paulo } 22955b9c547cSRui Paulo 22965b9c547cSRui Paulo participant->is_key_server = FALSE; 22975b9c547cSRui Paulo participant->principal = TRUE; 22985b9c547cSRui Paulo participant->is_elected = TRUE; 22995b9c547cSRui Paulo 23005b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &key_server->sci, 23015b9c547cSRui Paulo sizeof(kay->key_server_sci)); 23025b9c547cSRui Paulo kay->key_server_priority = key_server->key_server_priority; 2303780fb4a2SCy Schubert } else { 2304780fb4a2SCy Schubert participant->principal = FALSE; 2305780fb4a2SCy Schubert participant->is_key_server = FALSE; 2306780fb4a2SCy Schubert participant->is_elected = FALSE; 23075b9c547cSRui Paulo } 23085b9c547cSRui Paulo 23095b9c547cSRui Paulo return 0; 23105b9c547cSRui Paulo } 23115b9c547cSRui Paulo 23125b9c547cSRui Paulo 23135b9c547cSRui Paulo /** 23145b9c547cSRui Paulo * ieee802_1x_kay_decide_macsec_use - the key server determinate 23155b9c547cSRui Paulo * how to use MACsec: whether use MACsec and its capability 23165b9c547cSRui Paulo * protectFrames will be advised if the key server and one of its live peers are 23175b9c547cSRui Paulo * MACsec capable and one of those request MACsec protection 23185b9c547cSRui Paulo */ 23195b9c547cSRui Paulo static int 23205b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use( 23215b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 23225b9c547cSRui Paulo { 23235b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 23245b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 23255b9c547cSRui Paulo enum macsec_cap less_capability; 23265b9c547cSRui Paulo Boolean has_peer; 23275b9c547cSRui Paulo 23285b9c547cSRui Paulo if (!participant->is_key_server) 23295b9c547cSRui Paulo return -1; 23305b9c547cSRui Paulo 23315b9c547cSRui Paulo /* key server self is MACsec-desired and requesting MACsec */ 23325b9c547cSRui Paulo if (!kay->macsec_desired) { 23335b9c547cSRui Paulo participant->advised_desired = FALSE; 23345b9c547cSRui Paulo return -1; 23355b9c547cSRui Paulo } 23365b9c547cSRui Paulo if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 23375b9c547cSRui Paulo participant->advised_desired = FALSE; 23385b9c547cSRui Paulo return -1; 23395b9c547cSRui Paulo } 23405b9c547cSRui Paulo less_capability = kay->macsec_capable; 23415b9c547cSRui Paulo 23425b9c547cSRui Paulo /* at least one of peers is MACsec-desired and requesting MACsec */ 23435b9c547cSRui Paulo has_peer = FALSE; 23445b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 23455b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 23465b9c547cSRui Paulo if (!peer->macsec_desired) 23475b9c547cSRui Paulo continue; 23485b9c547cSRui Paulo 2349780fb4a2SCy Schubert if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED) 23505b9c547cSRui Paulo continue; 23515b9c547cSRui Paulo 2352780fb4a2SCy Schubert less_capability = (less_capability < peer->macsec_capability) ? 2353780fb4a2SCy Schubert less_capability : peer->macsec_capability; 23545b9c547cSRui Paulo has_peer = TRUE; 23555b9c547cSRui Paulo } 23565b9c547cSRui Paulo 23575b9c547cSRui Paulo if (has_peer) { 23585b9c547cSRui Paulo participant->advised_desired = TRUE; 23595b9c547cSRui Paulo participant->advised_capability = less_capability; 23605b9c547cSRui Paulo kay->authenticated = FALSE; 23615b9c547cSRui Paulo kay->secured = TRUE; 23625b9c547cSRui Paulo kay->failed = FALSE; 23635b9c547cSRui Paulo ieee802_1x_cp_connect_secure(kay->cp); 23645b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 23655b9c547cSRui Paulo } else { 23665b9c547cSRui Paulo participant->advised_desired = FALSE; 23675b9c547cSRui Paulo participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED; 23685b9c547cSRui Paulo participant->to_use_sak = FALSE; 23695b9c547cSRui Paulo kay->authenticated = TRUE; 23705b9c547cSRui Paulo kay->secured = FALSE; 23715b9c547cSRui Paulo kay->failed = FALSE; 23725b9c547cSRui Paulo kay->ltx_kn = 0; 23735b9c547cSRui Paulo kay->ltx_an = 0; 23745b9c547cSRui Paulo kay->lrx_kn = 0; 23755b9c547cSRui Paulo kay->lrx_an = 0; 23765b9c547cSRui Paulo kay->otx_kn = 0; 23775b9c547cSRui Paulo kay->otx_an = 0; 23785b9c547cSRui Paulo kay->orx_kn = 0; 23795b9c547cSRui Paulo kay->orx_an = 0; 23805b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp); 23815b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 23825b9c547cSRui Paulo } 23835b9c547cSRui Paulo 23845b9c547cSRui Paulo return 0; 23855b9c547cSRui Paulo } 23865b9c547cSRui Paulo 23875b9c547cSRui Paulo static const u8 pae_group_addr[ETH_ALEN] = { 23885b9c547cSRui Paulo 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 23895b9c547cSRui Paulo }; 23905b9c547cSRui Paulo 23915b9c547cSRui Paulo 23925b9c547cSRui Paulo /** 23935b9c547cSRui Paulo * ieee802_1x_kay_encode_mkpdu - 23945b9c547cSRui Paulo */ 23955b9c547cSRui Paulo static int 23965b9c547cSRui Paulo ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, 23975b9c547cSRui Paulo struct wpabuf *pbuf) 23985b9c547cSRui Paulo { 23995b9c547cSRui Paulo unsigned int i; 24005b9c547cSRui Paulo struct ieee8023_hdr *ether_hdr; 24015b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 24025b9c547cSRui Paulo 24035b9c547cSRui Paulo ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr)); 24045b9c547cSRui Paulo os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest)); 24055b9c547cSRui Paulo os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr, 24065b9c547cSRui Paulo sizeof(ether_hdr->dest)); 24075b9c547cSRui Paulo ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL); 2408*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR 2409*4bc52338SCy Schubert " Ethertype=0x%x", 2410*4bc52338SCy Schubert MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src), 2411*4bc52338SCy Schubert be_to_host16(ether_hdr->ethertype)); 24125b9c547cSRui Paulo 24135b9c547cSRui Paulo eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr)); 24145b9c547cSRui Paulo eapol_hdr->version = EAPOL_VERSION; 24155b9c547cSRui Paulo eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; 2416*4bc52338SCy Schubert eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf)); 2417*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 2418*4bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", 2419*4bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type, 2420*4bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 24215b9c547cSRui Paulo 2422780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { 2423780fb4a2SCy Schubert if (mka_body_handler[i].body_present && 2424780fb4a2SCy Schubert mka_body_handler[i].body_present(participant)) { 2425780fb4a2SCy Schubert if (mka_body_handler[i].body_tx(participant, pbuf)) 24265b9c547cSRui Paulo return -1; 24275b9c547cSRui Paulo } 24285b9c547cSRui Paulo } 24295b9c547cSRui Paulo 24305b9c547cSRui Paulo return 0; 24315b9c547cSRui Paulo } 24325b9c547cSRui Paulo 2433*4bc52338SCy Schubert 24345b9c547cSRui Paulo /** 24355b9c547cSRui Paulo * ieee802_1x_participant_send_mkpdu - 24365b9c547cSRui Paulo */ 24375b9c547cSRui Paulo static int 24385b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu( 24395b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 24405b9c547cSRui Paulo { 24415b9c547cSRui Paulo struct wpabuf *buf; 24425b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 24435b9c547cSRui Paulo size_t length = 0; 24445b9c547cSRui Paulo unsigned int i; 24455b9c547cSRui Paulo 2446*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)", 2447*4bc52338SCy Schubert kay->if_name); 24485b9c547cSRui Paulo length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); 2449780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { 2450780fb4a2SCy Schubert if (mka_body_handler[i].body_present && 2451780fb4a2SCy Schubert mka_body_handler[i].body_present(participant)) 2452780fb4a2SCy Schubert length += mka_body_handler[i].body_length(participant); 24535b9c547cSRui Paulo } 24545b9c547cSRui Paulo 24555b9c547cSRui Paulo buf = wpabuf_alloc(length); 24565b9c547cSRui Paulo if (!buf) { 24575b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: out of memory"); 24585b9c547cSRui Paulo return -1; 24595b9c547cSRui Paulo } 24605b9c547cSRui Paulo 24615b9c547cSRui Paulo if (ieee802_1x_kay_encode_mkpdu(participant, buf)) { 2462*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail"); 24635b9c547cSRui Paulo return -1; 24645b9c547cSRui Paulo } 24655b9c547cSRui Paulo 2466*4bc52338SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf); 24675b9c547cSRui Paulo l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf)); 24685b9c547cSRui Paulo wpabuf_free(buf); 24695b9c547cSRui Paulo 24705b9c547cSRui Paulo kay->active = TRUE; 24715b9c547cSRui Paulo participant->active = TRUE; 24725b9c547cSRui Paulo 24735b9c547cSRui Paulo return 0; 24745b9c547cSRui Paulo } 24755b9c547cSRui Paulo 24765b9c547cSRui Paulo 24775b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa); 247885732ac8SCy Schubert 247985732ac8SCy Schubert static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay, 248085732ac8SCy Schubert struct transmit_sa *sa) 248185732ac8SCy Schubert { 248285732ac8SCy Schubert secy_disable_transmit_sa(kay, sa); 248385732ac8SCy Schubert secy_delete_transmit_sa(kay, sa); 248485732ac8SCy Schubert ieee802_1x_kay_deinit_transmit_sa(sa); 248585732ac8SCy Schubert } 248685732ac8SCy Schubert 248785732ac8SCy Schubert 24885b9c547cSRui Paulo /** 24895b9c547cSRui Paulo * ieee802_1x_participant_timer - 24905b9c547cSRui Paulo */ 24915b9c547cSRui Paulo static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) 24925b9c547cSRui Paulo { 24935b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 24945b9c547cSRui Paulo struct ieee802_1x_kay *kay; 24955b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer, *pre_peer; 24965b9c547cSRui Paulo time_t now = time(NULL); 24975b9c547cSRui Paulo Boolean lp_changed; 24985b9c547cSRui Paulo struct receive_sc *rxsc, *pre_rxsc; 24995b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa; 25005b9c547cSRui Paulo 25015b9c547cSRui Paulo participant = (struct ieee802_1x_mka_participant *)eloop_ctx; 25025b9c547cSRui Paulo kay = participant->kay; 2503*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)", 2504*4bc52338SCy Schubert kay->if_name); 25055b9c547cSRui Paulo if (participant->cak_life) { 2506780fb4a2SCy Schubert if (now > participant->cak_life) 2507780fb4a2SCy Schubert goto delete_mka; 25085b9c547cSRui Paulo } 25095b9c547cSRui Paulo 25105b9c547cSRui Paulo /* should delete MKA instance if there are not live peers 25115b9c547cSRui Paulo * when the MKA life elapsed since its creating */ 25125b9c547cSRui Paulo if (participant->mka_life) { 25135b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 2514780fb4a2SCy Schubert if (now > participant->mka_life) 2515780fb4a2SCy Schubert goto delete_mka; 25165b9c547cSRui Paulo } else { 25175b9c547cSRui Paulo participant->mka_life = 0; 25185b9c547cSRui Paulo } 25195b9c547cSRui Paulo } 25205b9c547cSRui Paulo 25215b9c547cSRui Paulo lp_changed = FALSE; 25225b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->live_peers, 25235b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 25245b9c547cSRui Paulo if (now > peer->expire) { 25255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Live peer removed"); 25265b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, 25275b9c547cSRui Paulo sizeof(peer->mi)); 25285b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); 25295b9c547cSRui Paulo dl_list_for_each_safe(rxsc, pre_rxsc, 25305b9c547cSRui Paulo &participant->rxsc_list, 25315b9c547cSRui Paulo struct receive_sc, list) { 2532780fb4a2SCy Schubert if (sci_equal(&rxsc->sci, &peer->sci)) { 25335b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc( 25345b9c547cSRui Paulo participant, rxsc); 25355b9c547cSRui Paulo } 25365b9c547cSRui Paulo } 25375b9c547cSRui Paulo dl_list_del(&peer->list); 25385b9c547cSRui Paulo os_free(peer); 25395b9c547cSRui Paulo lp_changed = TRUE; 25405b9c547cSRui Paulo } 25415b9c547cSRui Paulo } 25425b9c547cSRui Paulo 25435b9c547cSRui Paulo if (lp_changed) { 25445b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 25455b9c547cSRui Paulo participant->advised_desired = FALSE; 25465b9c547cSRui Paulo participant->advised_capability = 25475b9c547cSRui Paulo MACSEC_CAP_NOT_IMPLEMENTED; 25485b9c547cSRui Paulo participant->to_use_sak = FALSE; 254985732ac8SCy Schubert participant->ltx = FALSE; 255085732ac8SCy Schubert participant->lrx = FALSE; 255185732ac8SCy Schubert participant->otx = FALSE; 255285732ac8SCy Schubert participant->orx = FALSE; 255385732ac8SCy Schubert participant->is_key_server = FALSE; 255485732ac8SCy Schubert participant->is_elected = FALSE; 255585732ac8SCy Schubert kay->authenticated = FALSE; 25565b9c547cSRui Paulo kay->secured = FALSE; 25575b9c547cSRui Paulo kay->failed = FALSE; 25585b9c547cSRui Paulo kay->ltx_kn = 0; 25595b9c547cSRui Paulo kay->ltx_an = 0; 25605b9c547cSRui Paulo kay->lrx_kn = 0; 25615b9c547cSRui Paulo kay->lrx_an = 0; 25625b9c547cSRui Paulo kay->otx_kn = 0; 25635b9c547cSRui Paulo kay->otx_an = 0; 25645b9c547cSRui Paulo kay->orx_kn = 0; 25655b9c547cSRui Paulo kay->orx_an = 0; 25665b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa, 25675b9c547cSRui Paulo &participant->txsc->sa_list, 25685b9c547cSRui Paulo struct transmit_sa, list) { 256985732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 25705b9c547cSRui Paulo } 25715b9c547cSRui Paulo 257285732ac8SCy Schubert ieee802_1x_cp_connect_pending(kay->cp); 25735b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 25745b9c547cSRui Paulo } else { 25755b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant); 25765b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant); 25775b9c547cSRui Paulo } 25785b9c547cSRui Paulo } 25795b9c547cSRui Paulo 25805b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers, 25815b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 25825b9c547cSRui Paulo if (now > peer->expire) { 25835b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Potential peer removed"); 25845b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, 25855b9c547cSRui Paulo sizeof(peer->mi)); 25865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); 25875b9c547cSRui Paulo dl_list_del(&peer->list); 25885b9c547cSRui Paulo os_free(peer); 25895b9c547cSRui Paulo } 25905b9c547cSRui Paulo } 25915b9c547cSRui Paulo 2592*4bc52338SCy Schubert if (participant->new_sak && participant->is_key_server) { 25935b9c547cSRui Paulo if (!ieee802_1x_kay_generate_new_sak(participant)) 25945b9c547cSRui Paulo participant->to_dist_sak = TRUE; 25955b9c547cSRui Paulo 25965b9c547cSRui Paulo participant->new_sak = FALSE; 25975b9c547cSRui Paulo } 25985b9c547cSRui Paulo 259985732ac8SCy Schubert if (participant->retry_count < MAX_RETRY_CNT || 260085732ac8SCy Schubert participant->mode == PSK) { 26015b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(participant); 26025b9c547cSRui Paulo participant->retry_count++; 26035b9c547cSRui Paulo } 26045b9c547cSRui Paulo 2605*4bc52338SCy Schubert eloop_register_timeout(kay->mka_hello_time / 1000, 0, 26065b9c547cSRui Paulo ieee802_1x_participant_timer, 26075b9c547cSRui Paulo participant, NULL); 2608780fb4a2SCy Schubert 2609780fb4a2SCy Schubert return; 2610780fb4a2SCy Schubert 2611780fb4a2SCy Schubert delete_mka: 2612780fb4a2SCy Schubert kay->authenticated = FALSE; 2613780fb4a2SCy Schubert kay->secured = FALSE; 2614780fb4a2SCy Schubert kay->failed = TRUE; 2615780fb4a2SCy Schubert ieee802_1x_kay_delete_mka(kay, &participant->ckn); 26165b9c547cSRui Paulo } 26175b9c547cSRui Paulo 26185b9c547cSRui Paulo 26195b9c547cSRui Paulo /** 26205b9c547cSRui Paulo * ieee802_1x_kay_init_transmit_sa - 26215b9c547cSRui Paulo */ 26225b9c547cSRui Paulo static struct transmit_sa * 26235b9c547cSRui Paulo ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, 26245b9c547cSRui Paulo struct data_key *key) 26255b9c547cSRui Paulo { 26265b9c547cSRui Paulo struct transmit_sa *psa; 26275b9c547cSRui Paulo 26285b9c547cSRui Paulo key->tx_latest = TRUE; 26295b9c547cSRui Paulo key->rx_latest = TRUE; 26305b9c547cSRui Paulo 26315b9c547cSRui Paulo psa = os_zalloc(sizeof(*psa)); 26325b9c547cSRui Paulo if (!psa) { 26335b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 26345b9c547cSRui Paulo return NULL; 26355b9c547cSRui Paulo } 26365b9c547cSRui Paulo 26375b9c547cSRui Paulo if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 && 26385b9c547cSRui Paulo key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50) 26395b9c547cSRui Paulo psa->confidentiality = TRUE; 26405b9c547cSRui Paulo else 26415b9c547cSRui Paulo psa->confidentiality = FALSE; 26425b9c547cSRui Paulo 26435b9c547cSRui Paulo psa->an = an; 264485732ac8SCy Schubert ieee802_1x_kay_use_data_key(key); 26455b9c547cSRui Paulo psa->pkey = key; 26465b9c547cSRui Paulo psa->next_pn = next_PN; 26475b9c547cSRui Paulo psa->sc = psc; 26485b9c547cSRui Paulo 26495b9c547cSRui Paulo os_get_time(&psa->created_time); 26505b9c547cSRui Paulo psa->in_use = FALSE; 26515b9c547cSRui Paulo 26525b9c547cSRui Paulo dl_list_add(&psc->sa_list, &psa->list); 26535b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 2654*4bc52338SCy Schubert "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC", 265585732ac8SCy Schubert an, next_PN); 26565b9c547cSRui Paulo 26575b9c547cSRui Paulo return psa; 26585b9c547cSRui Paulo } 26595b9c547cSRui Paulo 26605b9c547cSRui Paulo 26615b9c547cSRui Paulo /** 26625b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sa - 26635b9c547cSRui Paulo */ 26645b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) 26655b9c547cSRui Paulo { 266685732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(psa->pkey); 26675b9c547cSRui Paulo psa->pkey = NULL; 26685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 2669780fb4a2SCy Schubert "KaY: Delete transmit SA(an: %hhu) of SC", 2670780fb4a2SCy Schubert psa->an); 26715b9c547cSRui Paulo dl_list_del(&psa->list); 26725b9c547cSRui Paulo os_free(psa); 26735b9c547cSRui Paulo } 26745b9c547cSRui Paulo 26755b9c547cSRui Paulo 26765b9c547cSRui Paulo /** 26775b9c547cSRui Paulo * init_transmit_sc - 26785b9c547cSRui Paulo */ 26795b9c547cSRui Paulo static struct transmit_sc * 268085732ac8SCy Schubert ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) 26815b9c547cSRui Paulo { 26825b9c547cSRui Paulo struct transmit_sc *psc; 26835b9c547cSRui Paulo 26845b9c547cSRui Paulo psc = os_zalloc(sizeof(*psc)); 26855b9c547cSRui Paulo if (!psc) { 26865b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 26875b9c547cSRui Paulo return NULL; 26885b9c547cSRui Paulo } 26895b9c547cSRui Paulo os_memcpy(&psc->sci, sci, sizeof(psc->sci)); 26905b9c547cSRui Paulo 26915b9c547cSRui Paulo os_get_time(&psc->created_time); 26925b9c547cSRui Paulo psc->transmitting = FALSE; 26935b9c547cSRui Paulo psc->encoding_sa = FALSE; 26945b9c547cSRui Paulo psc->enciphering_sa = FALSE; 26955b9c547cSRui Paulo 26965b9c547cSRui Paulo dl_list_init(&psc->sa_list); 2697*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s", 2698*4bc52338SCy Schubert sci_txt(&psc->sci)); 26995b9c547cSRui Paulo 27005b9c547cSRui Paulo return psc; 27015b9c547cSRui Paulo } 27025b9c547cSRui Paulo 27035b9c547cSRui Paulo 27045b9c547cSRui Paulo /** 27055b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sc - 27065b9c547cSRui Paulo */ 27075b9c547cSRui Paulo static void 27085b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc( 27095b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc) 27105b9c547cSRui Paulo { 27115b9c547cSRui Paulo struct transmit_sa *psa, *tmp; 27125b9c547cSRui Paulo 271385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC"); 271485732ac8SCy Schubert dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list) 271585732ac8SCy Schubert ieee802_1x_delete_transmit_sa(participant->kay, psa); 27165b9c547cSRui Paulo 271785732ac8SCy Schubert secy_delete_transmit_sc(participant->kay, psc); 27185b9c547cSRui Paulo os_free(psc); 27195b9c547cSRui Paulo } 27205b9c547cSRui Paulo 27215b9c547cSRui Paulo 27225b9c547cSRui Paulo /****************** Interface between CP and KAY *********************/ 27235b9c547cSRui Paulo /** 27245b9c547cSRui Paulo * ieee802_1x_kay_set_latest_sa_attr - 27255b9c547cSRui Paulo */ 27265b9c547cSRui Paulo int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, 27275b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki, u8 lan, 27285b9c547cSRui Paulo Boolean ltx, Boolean lrx) 27295b9c547cSRui Paulo { 27305b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 27315b9c547cSRui Paulo 27325b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 27335b9c547cSRui Paulo if (!principal) 27345b9c547cSRui Paulo return -1; 27355b9c547cSRui Paulo 27365b9c547cSRui Paulo if (!lki) 27375b9c547cSRui Paulo os_memset(&principal->lki, 0, sizeof(principal->lki)); 27385b9c547cSRui Paulo else 27395b9c547cSRui Paulo os_memcpy(&principal->lki, lki, sizeof(principal->lki)); 27405b9c547cSRui Paulo 27415b9c547cSRui Paulo principal->lan = lan; 27425b9c547cSRui Paulo principal->ltx = ltx; 27435b9c547cSRui Paulo principal->lrx = lrx; 27445b9c547cSRui Paulo if (!lki) { 27455b9c547cSRui Paulo kay->ltx_kn = 0; 27465b9c547cSRui Paulo kay->lrx_kn = 0; 27475b9c547cSRui Paulo } else { 27485b9c547cSRui Paulo kay->ltx_kn = lki->kn; 27495b9c547cSRui Paulo kay->lrx_kn = lki->kn; 27505b9c547cSRui Paulo } 27515b9c547cSRui Paulo kay->ltx_an = lan; 27525b9c547cSRui Paulo kay->lrx_an = lan; 27535b9c547cSRui Paulo 27545b9c547cSRui Paulo return 0; 27555b9c547cSRui Paulo } 27565b9c547cSRui Paulo 27575b9c547cSRui Paulo 27585b9c547cSRui Paulo /** 27595b9c547cSRui Paulo * ieee802_1x_kay_set_old_sa_attr - 27605b9c547cSRui Paulo */ 27615b9c547cSRui Paulo int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, 27625b9c547cSRui Paulo struct ieee802_1x_mka_ki *oki, 27635b9c547cSRui Paulo u8 oan, Boolean otx, Boolean orx) 27645b9c547cSRui Paulo { 27655b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 27665b9c547cSRui Paulo 27675b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 27685b9c547cSRui Paulo if (!principal) 27695b9c547cSRui Paulo return -1; 27705b9c547cSRui Paulo 27715b9c547cSRui Paulo if (!oki) 27725b9c547cSRui Paulo os_memset(&principal->oki, 0, sizeof(principal->oki)); 27735b9c547cSRui Paulo else 27745b9c547cSRui Paulo os_memcpy(&principal->oki, oki, sizeof(principal->oki)); 27755b9c547cSRui Paulo 27765b9c547cSRui Paulo principal->oan = oan; 27775b9c547cSRui Paulo principal->otx = otx; 27785b9c547cSRui Paulo principal->orx = orx; 27795b9c547cSRui Paulo 27805b9c547cSRui Paulo if (!oki) { 27815b9c547cSRui Paulo kay->otx_kn = 0; 27825b9c547cSRui Paulo kay->orx_kn = 0; 27835b9c547cSRui Paulo } else { 27845b9c547cSRui Paulo kay->otx_kn = oki->kn; 27855b9c547cSRui Paulo kay->orx_kn = oki->kn; 27865b9c547cSRui Paulo } 27875b9c547cSRui Paulo kay->otx_an = oan; 27885b9c547cSRui Paulo kay->orx_an = oan; 27895b9c547cSRui Paulo 27905b9c547cSRui Paulo return 0; 27915b9c547cSRui Paulo } 27925b9c547cSRui Paulo 27935b9c547cSRui Paulo 279485732ac8SCy Schubert static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an) 279585732ac8SCy Schubert { 279685732ac8SCy Schubert struct transmit_sa *txsa; 279785732ac8SCy Schubert 279885732ac8SCy Schubert dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) { 279985732ac8SCy Schubert if (txsa->an == an) 280085732ac8SCy Schubert return txsa; 280185732ac8SCy Schubert } 280285732ac8SCy Schubert 280385732ac8SCy Schubert return NULL; 280485732ac8SCy Schubert } 280585732ac8SCy Schubert 280685732ac8SCy Schubert 280785732ac8SCy Schubert static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an) 280885732ac8SCy Schubert { 280985732ac8SCy Schubert struct receive_sa *rxsa; 281085732ac8SCy Schubert 281185732ac8SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) { 281285732ac8SCy Schubert if (rxsa->an == an) 281385732ac8SCy Schubert return rxsa; 281485732ac8SCy Schubert } 281585732ac8SCy Schubert 281685732ac8SCy Schubert return NULL; 281785732ac8SCy Schubert } 281885732ac8SCy Schubert 281985732ac8SCy Schubert 28205b9c547cSRui Paulo /** 28215b9c547cSRui Paulo * ieee802_1x_kay_create_sas - 28225b9c547cSRui Paulo */ 28235b9c547cSRui Paulo int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, 28245b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 28255b9c547cSRui Paulo { 28265b9c547cSRui Paulo struct data_key *sa_key, *latest_sak; 28275b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 28285b9c547cSRui Paulo struct receive_sc *rxsc; 28295b9c547cSRui Paulo struct receive_sa *rxsa; 28305b9c547cSRui Paulo struct transmit_sa *txsa; 28315b9c547cSRui Paulo 28325b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 28335b9c547cSRui Paulo if (!principal) 28345b9c547cSRui Paulo return -1; 28355b9c547cSRui Paulo 28365b9c547cSRui Paulo latest_sak = NULL; 28375b9c547cSRui Paulo dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) { 28385b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, lki)) { 28395b9c547cSRui Paulo sa_key->rx_latest = TRUE; 28405b9c547cSRui Paulo sa_key->tx_latest = TRUE; 28415b9c547cSRui Paulo latest_sak = sa_key; 28425b9c547cSRui Paulo principal->to_use_sak = TRUE; 28435b9c547cSRui Paulo } else { 28445b9c547cSRui Paulo sa_key->rx_latest = FALSE; 28455b9c547cSRui Paulo sa_key->tx_latest = FALSE; 28465b9c547cSRui Paulo } 28475b9c547cSRui Paulo } 28485b9c547cSRui Paulo if (!latest_sak) { 2849*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: lki related sak not found"); 28505b9c547cSRui Paulo return -1; 28515b9c547cSRui Paulo } 28525b9c547cSRui Paulo 28535b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 285485732ac8SCy Schubert while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL) 285585732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa); 285685732ac8SCy Schubert 28575b9c547cSRui Paulo rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1, 28585b9c547cSRui Paulo latest_sak); 28595b9c547cSRui Paulo if (!rxsa) 28605b9c547cSRui Paulo return -1; 28615b9c547cSRui Paulo 28625b9c547cSRui Paulo secy_create_receive_sa(kay, rxsa); 28635b9c547cSRui Paulo } 28645b9c547cSRui Paulo 286585732ac8SCy Schubert while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) != 286685732ac8SCy Schubert NULL) 286785732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 286885732ac8SCy Schubert 28695b9c547cSRui Paulo txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, 2870*4bc52338SCy Schubert latest_sak->next_pn ? 2871*4bc52338SCy Schubert latest_sak->next_pn : 1, 2872*4bc52338SCy Schubert latest_sak); 28735b9c547cSRui Paulo if (!txsa) 28745b9c547cSRui Paulo return -1; 28755b9c547cSRui Paulo 28765b9c547cSRui Paulo secy_create_transmit_sa(kay, txsa); 28775b9c547cSRui Paulo 28785b9c547cSRui Paulo 28795b9c547cSRui Paulo 28805b9c547cSRui Paulo return 0; 28815b9c547cSRui Paulo } 28825b9c547cSRui Paulo 28835b9c547cSRui Paulo 28845b9c547cSRui Paulo /** 28855b9c547cSRui Paulo * ieee802_1x_kay_delete_sas - 28865b9c547cSRui Paulo */ 28875b9c547cSRui Paulo int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, 28885b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki) 28895b9c547cSRui Paulo { 28905b9c547cSRui Paulo struct data_key *sa_key, *pre_key; 28915b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa; 28925b9c547cSRui Paulo struct receive_sa *rxsa, *pre_rxsa; 28935b9c547cSRui Paulo struct receive_sc *rxsc; 28945b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 28955b9c547cSRui Paulo 28965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__); 28975b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 28985b9c547cSRui Paulo if (!principal) 28995b9c547cSRui Paulo return -1; 29005b9c547cSRui Paulo 29015b9c547cSRui Paulo /* remove the transmit sa */ 29025b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list, 29035b9c547cSRui Paulo struct transmit_sa, list) { 290485732ac8SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki)) 290585732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 29065b9c547cSRui Paulo } 29075b9c547cSRui Paulo 29085b9c547cSRui Paulo /* remove the receive sa */ 29095b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 29105b9c547cSRui Paulo dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list, 29115b9c547cSRui Paulo struct receive_sa, list) { 291285732ac8SCy Schubert if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) 291385732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa); 29145b9c547cSRui Paulo } 29155b9c547cSRui Paulo } 29165b9c547cSRui Paulo 29175b9c547cSRui Paulo /* remove the sak */ 29185b9c547cSRui Paulo dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, 29195b9c547cSRui Paulo struct data_key, list) { 29205b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, ki)) { 2921*4bc52338SCy Schubert if (principal->new_key == sa_key) 2922*4bc52338SCy Schubert principal->new_key = NULL; 292385732ac8SCy Schubert dl_list_del(&sa_key->list); 29245b9c547cSRui Paulo ieee802_1x_kay_deinit_data_key(sa_key); 29255b9c547cSRui Paulo break; 29265b9c547cSRui Paulo } 29275b9c547cSRui Paulo } 29285b9c547cSRui Paulo 29295b9c547cSRui Paulo return 0; 29305b9c547cSRui Paulo } 29315b9c547cSRui Paulo 29325b9c547cSRui Paulo 29335b9c547cSRui Paulo /** 29345b9c547cSRui Paulo * ieee802_1x_kay_enable_tx_sas - 29355b9c547cSRui Paulo */ 29365b9c547cSRui Paulo int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, 29375b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 29385b9c547cSRui Paulo { 29395b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29405b9c547cSRui Paulo struct transmit_sa *txsa; 29415b9c547cSRui Paulo 29425b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 29435b9c547cSRui Paulo if (!principal) 29445b9c547cSRui Paulo return -1; 29455b9c547cSRui Paulo 29465b9c547cSRui Paulo dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa, 29475b9c547cSRui Paulo list) { 29485b9c547cSRui Paulo if (is_ki_equal(&txsa->pkey->key_identifier, lki)) { 29495b9c547cSRui Paulo txsa->in_use = TRUE; 29505b9c547cSRui Paulo secy_enable_transmit_sa(kay, txsa); 29515b9c547cSRui Paulo ieee802_1x_cp_set_usingtransmitas( 29525b9c547cSRui Paulo principal->kay->cp, TRUE); 29535b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp); 29545b9c547cSRui Paulo } 29555b9c547cSRui Paulo } 29565b9c547cSRui Paulo 29575b9c547cSRui Paulo return 0; 29585b9c547cSRui Paulo } 29595b9c547cSRui Paulo 29605b9c547cSRui Paulo 29615b9c547cSRui Paulo /** 29625b9c547cSRui Paulo * ieee802_1x_kay_enable_rx_sas - 29635b9c547cSRui Paulo */ 29645b9c547cSRui Paulo int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, 29655b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 29665b9c547cSRui Paulo { 29675b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29685b9c547cSRui Paulo struct receive_sa *rxsa; 29695b9c547cSRui Paulo struct receive_sc *rxsc; 29705b9c547cSRui Paulo 29715b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 29725b9c547cSRui Paulo if (!principal) 29735b9c547cSRui Paulo return -1; 29745b9c547cSRui Paulo 29755b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 29765b9c547cSRui Paulo dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) 29775b9c547cSRui Paulo { 29785b9c547cSRui Paulo if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) { 29795b9c547cSRui Paulo rxsa->in_use = TRUE; 29805b9c547cSRui Paulo secy_enable_receive_sa(kay, rxsa); 29815b9c547cSRui Paulo ieee802_1x_cp_set_usingreceivesas( 29825b9c547cSRui Paulo principal->kay->cp, TRUE); 29835b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp); 29845b9c547cSRui Paulo } 29855b9c547cSRui Paulo } 29865b9c547cSRui Paulo } 29875b9c547cSRui Paulo 29885b9c547cSRui Paulo return 0; 29895b9c547cSRui Paulo } 29905b9c547cSRui Paulo 29915b9c547cSRui Paulo 29925b9c547cSRui Paulo /** 29935b9c547cSRui Paulo * ieee802_1x_kay_enable_new_info - 29945b9c547cSRui Paulo */ 29955b9c547cSRui Paulo int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) 29965b9c547cSRui Paulo { 29975b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29985b9c547cSRui Paulo 29995b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 30005b9c547cSRui Paulo if (!principal) 30015b9c547cSRui Paulo return -1; 30025b9c547cSRui Paulo 300385732ac8SCy Schubert if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) { 30045b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(principal); 30055b9c547cSRui Paulo principal->retry_count++; 30065b9c547cSRui Paulo } 30075b9c547cSRui Paulo 30085b9c547cSRui Paulo return 0; 30095b9c547cSRui Paulo } 30105b9c547cSRui Paulo 30115b9c547cSRui Paulo 30125b9c547cSRui Paulo /** 30135b9c547cSRui Paulo * ieee802_1x_kay_mkpdu_sanity_check - 3014*4bc52338SCy Schubert * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of 3015*4bc52338SCy Schubert * MKPDUs) 30165b9c547cSRui Paulo */ 30175b9c547cSRui Paulo static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, 30185b9c547cSRui Paulo const u8 *buf, size_t len) 30195b9c547cSRui Paulo { 30205b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr; 30215b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 30225b9c547cSRui Paulo struct ieee802_1x_mka_hdr *mka_hdr; 30235b9c547cSRui Paulo struct ieee802_1x_mka_basic_body *body; 30245b9c547cSRui Paulo size_t mka_msg_len; 30255b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 30265b9c547cSRui Paulo size_t body_len; 302785732ac8SCy Schubert size_t ckn_len; 30285b9c547cSRui Paulo u8 icv[MAX_ICV_LEN]; 3029*4bc52338SCy Schubert const u8 *msg_icv; 30305b9c547cSRui Paulo 3031*4bc52338SCy Schubert /* len > eth+eapol header already verified in kay_l2_receive(); 3032*4bc52338SCy Schubert * likewise, eapol_hdr->length validated there */ 30335b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf; 30345b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); 30355b9c547cSRui Paulo mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1); 30365b9c547cSRui Paulo 3037*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR 3038*4bc52338SCy Schubert " Ethertype=0x%x", 3039*4bc52338SCy Schubert MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src), 3040*4bc52338SCy Schubert be_to_host16(eth_hdr->ethertype)); 3041*4bc52338SCy Schubert 3042*4bc52338SCy Schubert /* the destination address shall not be an individual address */ 30435b9c547cSRui Paulo if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) { 3044*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 30455b9c547cSRui Paulo "KaY: ethernet destination address is not PAE group address"); 30465b9c547cSRui Paulo return -1; 30475b9c547cSRui Paulo } 30485b9c547cSRui Paulo 3049*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 3050*4bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", 3051*4bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type, 3052*4bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 3053*4bc52338SCy Schubert 3054*4bc52338SCy Schubert /* MKPDU shall not be less than 32 octets */ 30555b9c547cSRui Paulo mka_msg_len = be_to_host16(eapol_hdr->length); 30565b9c547cSRui Paulo if (mka_msg_len < 32) { 3057*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets"); 30585b9c547cSRui Paulo return -1; 30595b9c547cSRui Paulo } 3060*4bc52338SCy Schubert /* MKPDU shall be a multiple of 4 octets */ 30615b9c547cSRui Paulo if ((mka_msg_len % 4) != 0) { 3062*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 30635b9c547cSRui Paulo "KaY: MKPDU is not multiple of 4 octets"); 30645b9c547cSRui Paulo return -1; 30655b9c547cSRui Paulo } 30665b9c547cSRui Paulo 3067*4bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)", 3068*4bc52338SCy Schubert mka_hdr, mka_msg_len); 3069*4bc52338SCy Schubert 3070*4bc52338SCy Schubert /* Room for body_len already verified in kay_l2_receive() */ 30715b9c547cSRui Paulo body = (struct ieee802_1x_mka_basic_body *) mka_hdr; 30725b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 30735b9c547cSRui Paulo /* EAPOL-MKA body should comprise basic parameter set and ICV */ 30745b9c547cSRui Paulo if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { 30755b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3076780fb4a2SCy Schubert "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", 3077780fb4a2SCy Schubert mka_msg_len, MKA_HDR_LEN, 3078780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN); 30795b9c547cSRui Paulo return -1; 30805b9c547cSRui Paulo } 30815b9c547cSRui Paulo 308285732ac8SCy Schubert if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { 308385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", 308485732ac8SCy Schubert body_len); 308585732ac8SCy Schubert return -1; 308685732ac8SCy Schubert } 308785732ac8SCy Schubert ckn_len = body_len - 308885732ac8SCy Schubert (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); 308985732ac8SCy Schubert if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { 3090*4bc52338SCy Schubert wpa_printf(MSG_WARNING, 309185732ac8SCy Schubert "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", 309285732ac8SCy Schubert ckn_len, MAX_CKN_LEN); 309385732ac8SCy Schubert return -1; 309485732ac8SCy Schubert } 309585732ac8SCy Schubert 3096*4bc52338SCy Schubert ieee802_1x_mka_dump_basic_body(body); 3097*4bc52338SCy Schubert 30985b9c547cSRui Paulo /* CKN should be owned by I */ 309985732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); 31005b9c547cSRui Paulo if (!participant) { 3101*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA"); 31025b9c547cSRui Paulo return -1; 31035b9c547cSRui Paulo } 31045b9c547cSRui Paulo 31055b9c547cSRui Paulo /* algorithm agility check */ 31065b9c547cSRui Paulo if (os_memcmp(body->algo_agility, mka_algo_agility, 31075b9c547cSRui Paulo sizeof(body->algo_agility)) != 0) { 3108*4bc52338SCy Schubert wpa_printf(MSG_INFO, 3109*4bc52338SCy Schubert "KaY: Peer's algorithm agility (%s) not supported", 3110*4bc52338SCy Schubert algo_agility_txt(body->algo_agility)); 31115b9c547cSRui Paulo return -1; 31125b9c547cSRui Paulo } 31135b9c547cSRui Paulo 31145b9c547cSRui Paulo /* ICV check */ 31155b9c547cSRui Paulo /* 31165b9c547cSRui Paulo * The ICV will comprise the final octets of the packet body, whatever 31175b9c547cSRui Paulo * its size, not the fixed length 16 octets, indicated by the EAPOL 31185b9c547cSRui Paulo * packet body length. 31195b9c547cSRui Paulo */ 3120*4bc52338SCy Schubert if (len < mka_alg_tbl[kay->mka_algindex].icv_len || 3121*4bc52338SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_hash( 3122*4bc52338SCy Schubert participant->ick.key, participant->ick.len, 31235b9c547cSRui Paulo buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) { 3124*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV"); 31255b9c547cSRui Paulo return -1; 31265b9c547cSRui Paulo } 3127780fb4a2SCy Schubert 3128*4bc52338SCy Schubert msg_icv = ieee802_1x_mka_decode_icv_body(participant, 3129*4bc52338SCy Schubert (const u8 *) mka_hdr, 31305b9c547cSRui Paulo mka_msg_len); 3131780fb4a2SCy Schubert if (!msg_icv) { 3132*4bc52338SCy Schubert wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it"); 31335b9c547cSRui Paulo return -1; 31345b9c547cSRui Paulo } 3135*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Received ICV", 3136*4bc52338SCy Schubert msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len); 3137780fb4a2SCy Schubert if (os_memcmp_const(msg_icv, icv, 3138780fb4a2SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { 3139*4bc52338SCy Schubert wpa_printf(MSG_WARNING, 3140780fb4a2SCy Schubert "KaY: Computed ICV is not equal to Received ICV"); 3141*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV", 3142*4bc52338SCy Schubert icv, mka_alg_tbl[kay->mka_algindex].icv_len); 31435b9c547cSRui Paulo return -1; 31445b9c547cSRui Paulo } 31455b9c547cSRui Paulo 31465b9c547cSRui Paulo return 0; 31475b9c547cSRui Paulo } 31485b9c547cSRui Paulo 31495b9c547cSRui Paulo 31505b9c547cSRui Paulo /** 31515b9c547cSRui Paulo * ieee802_1x_kay_decode_mkpdu - 31525b9c547cSRui Paulo */ 31535b9c547cSRui Paulo static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, 31545b9c547cSRui Paulo const u8 *buf, size_t len) 31555b9c547cSRui Paulo { 31565b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 31575b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 3158*4bc52338SCy Schubert struct ieee802_1x_kay_peer *peer; 31595b9c547cSRui Paulo size_t body_len; 31605b9c547cSRui Paulo size_t left_len; 3161780fb4a2SCy Schubert u8 body_type; 31625b9c547cSRui Paulo int i; 31635b9c547cSRui Paulo const u8 *pos; 31645b9c547cSRui Paulo Boolean handled[256]; 3165*4bc52338SCy Schubert Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use 3166*4bc52338SCy Schubert * parameter set */ 3167*4bc52338SCy Schubert Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer; 31685b9c547cSRui Paulo 3169*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)", 3170*4bc52338SCy Schubert kay->if_name); 31715b9c547cSRui Paulo if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) 31725b9c547cSRui Paulo return -1; 31735b9c547cSRui Paulo 31745b9c547cSRui Paulo /* handle basic parameter set */ 31755b9c547cSRui Paulo pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr); 31765b9c547cSRui Paulo left_len = len - sizeof(struct ieee8023_hdr) - 31775b9c547cSRui Paulo sizeof(struct ieee802_1x_hdr); 31785b9c547cSRui Paulo participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len); 31795b9c547cSRui Paulo if (!participant) 31805b9c547cSRui Paulo return -1; 31815b9c547cSRui Paulo 31825b9c547cSRui Paulo /* to skip basic parameter set */ 31835b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos; 3184*4bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 3185*4bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN) 3186*4bc52338SCy Schubert return -1; 31875b9c547cSRui Paulo pos += body_len + MKA_HDR_LEN; 31885b9c547cSRui Paulo left_len -= body_len + MKA_HDR_LEN; 31895b9c547cSRui Paulo 31905b9c547cSRui Paulo /* check i am in the peer's peer list */ 3191*4bc52338SCy Schubert i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos, 3192*4bc52338SCy Schubert left_len); 3193*4bc52338SCy Schubert is_in_live_peer = ieee802_1x_kay_is_in_live_peer( 3194*4bc52338SCy Schubert participant, participant->current_peer_id.mi); 3195*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s", 3196*4bc52338SCy Schubert yes_no(i_in_peerlist), yes_no(is_in_live_peer)); 3197*4bc52338SCy Schubert if (i_in_peerlist && !is_in_live_peer) { 3198780fb4a2SCy Schubert /* accept the peer as live peer */ 3199*4bc52338SCy Schubert is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer( 3200*4bc52338SCy Schubert participant, participant->current_peer_id.mi); 3201*4bc52338SCy Schubert if (is_in_potential_peer) { 3202780fb4a2SCy Schubert if (!ieee802_1x_kay_move_live_peer( 3203780fb4a2SCy Schubert participant, 3204780fb4a2SCy Schubert participant->current_peer_id.mi, 3205780fb4a2SCy Schubert be_to_host32(participant-> 3206780fb4a2SCy Schubert current_peer_id.mn))) 3207780fb4a2SCy Schubert return -1; 3208780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_live_peer( 32095b9c547cSRui Paulo participant, participant->current_peer_id.mi, 3210780fb4a2SCy Schubert be_to_host32(participant-> 3211780fb4a2SCy Schubert current_peer_id.mn))) { 3212780fb4a2SCy Schubert return -1; 3213780fb4a2SCy Schubert } 3214780fb4a2SCy Schubert 32155b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant); 32165b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant); 32175b9c547cSRui Paulo } 32185b9c547cSRui Paulo 32195b9c547cSRui Paulo /* 32205b9c547cSRui Paulo * Handle other parameter set than basic parameter set. 32215b9c547cSRui Paulo * Each parameter set should be present only once. 32225b9c547cSRui Paulo */ 32235b9c547cSRui Paulo for (i = 0; i < 256; i++) 32245b9c547cSRui Paulo handled[i] = FALSE; 32255b9c547cSRui Paulo 32265b9c547cSRui Paulo handled[0] = TRUE; 3227780fb4a2SCy Schubert for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; 3228780fb4a2SCy Schubert pos += body_len + MKA_HDR_LEN, 3229780fb4a2SCy Schubert left_len -= body_len + MKA_HDR_LEN) { 32305b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos; 3231*4bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 32325b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr); 32335b9c547cSRui Paulo 32345b9c547cSRui Paulo if (body_type == MKA_ICV_INDICATOR) 32355b9c547cSRui Paulo return 0; 32365b9c547cSRui Paulo 32375b9c547cSRui Paulo if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { 32385b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3239780fb4a2SCy Schubert "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", 3240780fb4a2SCy Schubert left_len, MKA_HDR_LEN, 3241780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN); 324285732ac8SCy Schubert return -1; 32435b9c547cSRui Paulo } 32445b9c547cSRui Paulo 3245*4bc52338SCy Schubert if (handled[body_type]) { 3246*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 3247*4bc52338SCy Schubert "KaY: Ignore duplicated body type %u", 3248*4bc52338SCy Schubert body_type); 3249780fb4a2SCy Schubert continue; 3250*4bc52338SCy Schubert } 32515b9c547cSRui Paulo 32525b9c547cSRui Paulo handled[body_type] = TRUE; 3253780fb4a2SCy Schubert if (body_type < ARRAY_SIZE(mka_body_handler) && 3254780fb4a2SCy Schubert mka_body_handler[body_type].body_rx) { 3255*4bc52338SCy Schubert if (mka_body_handler[body_type].body_rx 3256*4bc52338SCy Schubert (participant, pos, left_len) != 0) { 3257*4bc52338SCy Schubert /* Handle parameter set failure */ 3258*4bc52338SCy Schubert if (body_type != MKA_SAK_USE) { 3259*4bc52338SCy Schubert wpa_printf(MSG_INFO, 3260*4bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", 3261*4bc52338SCy Schubert body_type); 3262*4bc52338SCy Schubert return -1; 3263*4bc52338SCy Schubert } 3264*4bc52338SCy Schubert 3265*4bc52338SCy Schubert /* Ideally DIST-SAK should be processed before 3266*4bc52338SCy Schubert * SAK-USE. Unfortunately IEEE Std 802.1X-2010, 3267*4bc52338SCy Schubert * 11.11.3 (Encoding MKPDUs) states SAK-USE(3) 3268*4bc52338SCy Schubert * must always be encoded before DIST-SAK(4). 3269*4bc52338SCy Schubert * Rather than redesigning mka_body_handler so 3270*4bc52338SCy Schubert * that it somehow processes DIST-SAK before 3271*4bc52338SCy Schubert * SAK-USE, just ignore SAK-USE failures if 3272*4bc52338SCy Schubert * DIST-SAK is also present in this MKPDU. */ 3273*4bc52338SCy Schubert bad_sak_use = TRUE; 3274*4bc52338SCy Schubert } 32755b9c547cSRui Paulo } else { 32765b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3277*4bc52338SCy Schubert "KaY: The body type %d is not supported in this MKA version %d", 32785b9c547cSRui Paulo body_type, MKA_VERSION_ID); 32795b9c547cSRui Paulo } 32805b9c547cSRui Paulo } 32815b9c547cSRui Paulo 3282*4bc52338SCy Schubert if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) { 3283*4bc52338SCy Schubert wpa_printf(MSG_INFO, 3284*4bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", 3285*4bc52338SCy Schubert MKA_SAK_USE); 3286*4bc52338SCy Schubert if (!reset_participant_mi(participant)) 3287*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 3288*4bc52338SCy Schubert else 3289*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 3290*4bc52338SCy Schubert "KaY: Selected a new random MI: %s", 3291*4bc52338SCy Schubert mi_txt(participant->mi)); 3292*4bc52338SCy Schubert return -1; 3293*4bc52338SCy Schubert } 3294*4bc52338SCy Schubert 3295*4bc52338SCy Schubert /* Detect missing parameter sets */ 3296*4bc52338SCy Schubert peer = ieee802_1x_kay_get_live_peer(participant, 3297*4bc52338SCy Schubert participant->current_peer_id.mi); 3298*4bc52338SCy Schubert if (peer) { 3299*4bc52338SCy Schubert /* MKPDU is from live peer */ 3300*4bc52338SCy Schubert if (!handled[MKA_SAK_USE]) { 3301*4bc52338SCy Schubert /* Once a live peer starts sending SAK-USE, it should be 3302*4bc52338SCy Schubert * sent every time. */ 3303*4bc52338SCy Schubert if (peer->sak_used) { 3304*4bc52338SCy Schubert wpa_printf(MSG_INFO, 3305*4bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE"); 3306*4bc52338SCy Schubert return -1; 3307*4bc52338SCy Schubert } 3308*4bc52338SCy Schubert 3309*4bc52338SCy Schubert /* Live peer is probably hung if it hasn't sent SAK-USE 3310*4bc52338SCy Schubert * after a reasonable number of MKPDUs. Drop the MKPDU, 3311*4bc52338SCy Schubert * which will eventually force an timeout. */ 3312*4bc52338SCy Schubert if (++peer->missing_sak_use_count > 3313*4bc52338SCy Schubert MAX_MISSING_SAK_USE) { 3314*4bc52338SCy Schubert wpa_printf(MSG_INFO, 3315*4bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE"); 3316*4bc52338SCy Schubert return -1; 3317*4bc52338SCy Schubert } 3318*4bc52338SCy Schubert } else { 3319*4bc52338SCy Schubert peer->missing_sak_use_count = 0; 3320*4bc52338SCy Schubert 3321*4bc52338SCy Schubert /* Only update live peer watchdog after successful 3322*4bc52338SCy Schubert * decode of all parameter sets */ 3323*4bc52338SCy Schubert peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; 3324*4bc52338SCy Schubert } 3325*4bc52338SCy Schubert } else { 3326*4bc52338SCy Schubert /* MKPDU is from new or potential peer */ 3327*4bc52338SCy Schubert peer = ieee802_1x_kay_get_peer(participant, 3328*4bc52338SCy Schubert participant->current_peer_id.mi); 3329*4bc52338SCy Schubert if (!peer) { 3330*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: No peer entry found"); 3331*4bc52338SCy Schubert return -1; 3332*4bc52338SCy Schubert } 3333*4bc52338SCy Schubert 3334*4bc52338SCy Schubert /* Do not update potential peer watchdog. Per IEEE Std 3335*4bc52338SCy Schubert * 802.1X-2010, 9.4.3, potential peers need to show liveness by 3336*4bc52338SCy Schubert * including our MI/MN in their transmitted MKPDU (within 3337*4bc52338SCy Schubert * potential or live parameter sets). Whena potential peer does 3338*4bc52338SCy Schubert * include our MI/MN in an MKPDU, we respond by moving the peer 3339*4bc52338SCy Schubert * from 'potential_peers' to 'live_peers'. */ 3340*4bc52338SCy Schubert } 3341*4bc52338SCy Schubert 33425b9c547cSRui Paulo kay->active = TRUE; 33435b9c547cSRui Paulo participant->retry_count = 0; 33445b9c547cSRui Paulo participant->active = TRUE; 33455b9c547cSRui Paulo 33465b9c547cSRui Paulo return 0; 33475b9c547cSRui Paulo } 33485b9c547cSRui Paulo 33495b9c547cSRui Paulo 33505b9c547cSRui Paulo 33515b9c547cSRui Paulo static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, 33525b9c547cSRui Paulo size_t len) 33535b9c547cSRui Paulo { 33545b9c547cSRui Paulo struct ieee802_1x_kay *kay = ctx; 33555b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr; 33565b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 3357*4bc52338SCy Schubert size_t calc_len; 3358*4bc52338SCy Schubert 3359*4bc52338SCy Schubert /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */ 33605b9c547cSRui Paulo 33615b9c547cSRui Paulo /* must contain at least ieee8023_hdr + ieee802_1x_hdr */ 33625b9c547cSRui Paulo if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) { 33635b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)", 33645b9c547cSRui Paulo (unsigned long) len); 33655b9c547cSRui Paulo return; 33665b9c547cSRui Paulo } 33675b9c547cSRui Paulo 33685b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf; 33695b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); 3370*4bc52338SCy Schubert calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) + 3371*4bc52338SCy Schubert be_to_host16(eapol_hdr->length); 3372*4bc52338SCy Schubert if (len < calc_len) { 3373*4bc52338SCy Schubert wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)", 33745b9c547cSRui Paulo (unsigned long) len, 3375*4bc52338SCy Schubert (unsigned long) calc_len, 3376*4bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 33775b9c547cSRui Paulo return; 33785b9c547cSRui Paulo } 3379*4bc52338SCy Schubert if (len > calc_len) { 3380*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, 3381*4bc52338SCy Schubert "KaY: Ignore extra octets following the Packey Body field", 3382*4bc52338SCy Schubert &buf[calc_len], len - calc_len); 3383*4bc52338SCy Schubert len = calc_len; 3384*4bc52338SCy Schubert } 33855b9c547cSRui Paulo 33865b9c547cSRui Paulo if (eapol_hdr->version < EAPOL_VERSION) { 33875b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA", 33885b9c547cSRui Paulo eapol_hdr->version); 33895b9c547cSRui Paulo return; 33905b9c547cSRui Paulo } 3391780fb4a2SCy Schubert if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || 33925b9c547cSRui Paulo eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) 3393*4bc52338SCy Schubert return; /* ignore other EAPOL types silently here */ 33945b9c547cSRui Paulo 3395*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len); 33965b9c547cSRui Paulo if (dl_list_empty(&kay->participant_list)) { 3397*4bc52338SCy Schubert wpa_printf(MSG_ERROR, 3398*4bc52338SCy Schubert "KaY: No MKA participant instance - ignore EAPOL-MKA"); 33995b9c547cSRui Paulo return; 34005b9c547cSRui Paulo } 34015b9c547cSRui Paulo 34025b9c547cSRui Paulo ieee802_1x_kay_decode_mkpdu(kay, buf, len); 34035b9c547cSRui Paulo } 34045b9c547cSRui Paulo 34055b9c547cSRui Paulo 34065b9c547cSRui Paulo /** 34075b9c547cSRui Paulo * ieee802_1x_kay_init - 34085b9c547cSRui Paulo */ 34095b9c547cSRui Paulo struct ieee802_1x_kay * 34105b9c547cSRui Paulo ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, 3411*4bc52338SCy Schubert Boolean macsec_replay_protect, u32 macsec_replay_window, 341285732ac8SCy Schubert u16 port, u8 priority, const char *ifname, const u8 *addr) 34135b9c547cSRui Paulo { 34145b9c547cSRui Paulo struct ieee802_1x_kay *kay; 34155b9c547cSRui Paulo 3416*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR 3417*4bc52338SCy Schubert " port=%u priority=%u", 3418*4bc52338SCy Schubert ifname, MAC2STR(addr), port, priority); 34195b9c547cSRui Paulo kay = os_zalloc(sizeof(*kay)); 34205b9c547cSRui Paulo if (!kay) { 34215b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); 342285732ac8SCy Schubert os_free(ctx); 34235b9c547cSRui Paulo return NULL; 34245b9c547cSRui Paulo } 34255b9c547cSRui Paulo 34265b9c547cSRui Paulo kay->ctx = ctx; 34275b9c547cSRui Paulo 34285b9c547cSRui Paulo kay->enable = TRUE; 34295b9c547cSRui Paulo kay->active = FALSE; 34305b9c547cSRui Paulo 34315b9c547cSRui Paulo kay->authenticated = FALSE; 34325b9c547cSRui Paulo kay->secured = FALSE; 34335b9c547cSRui Paulo kay->failed = FALSE; 34345b9c547cSRui Paulo kay->policy = policy; 34355b9c547cSRui Paulo 34365b9c547cSRui Paulo os_strlcpy(kay->if_name, ifname, IFNAMSIZ); 34375b9c547cSRui Paulo os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); 343885732ac8SCy Schubert kay->actor_sci.port = host_to_be16(port ? port : 0x0001); 3439*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s", 3440*4bc52338SCy Schubert sci_txt(&kay->actor_sci)); 344185732ac8SCy Schubert kay->actor_priority = priority; 34425b9c547cSRui Paulo 34435b9c547cSRui Paulo /* While actor acts as a key server, shall distribute sakey */ 34445b9c547cSRui Paulo kay->dist_kn = 1; 34455b9c547cSRui Paulo kay->dist_an = 0; 34465b9c547cSRui Paulo kay->dist_time = 0; 34475b9c547cSRui Paulo 34485b9c547cSRui Paulo kay->pn_exhaustion = PENDING_PN_EXHAUSTION; 34495b9c547cSRui Paulo kay->macsec_csindex = DEFAULT_CS_INDEX; 34505b9c547cSRui Paulo kay->mka_algindex = DEFAULT_MKA_ALG_INDEX; 34515b9c547cSRui Paulo kay->mka_version = MKA_VERSION_ID; 34525b9c547cSRui Paulo 34535b9c547cSRui Paulo os_memcpy(kay->algo_agility, mka_algo_agility, 34545b9c547cSRui Paulo sizeof(kay->algo_agility)); 34555b9c547cSRui Paulo 34565b9c547cSRui Paulo dl_list_init(&kay->participant_list); 34575b9c547cSRui Paulo 345885732ac8SCy Schubert if (policy != DO_NOT_SECURE && 345985732ac8SCy Schubert secy_get_capability(kay, &kay->macsec_capable) < 0) 346085732ac8SCy Schubert goto error; 346185732ac8SCy Schubert 346285732ac8SCy Schubert if (policy == DO_NOT_SECURE || 346385732ac8SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 34645b9c547cSRui Paulo kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; 34655b9c547cSRui Paulo kay->macsec_desired = FALSE; 34665b9c547cSRui Paulo kay->macsec_protect = FALSE; 3467*4bc52338SCy Schubert kay->macsec_encrypt = FALSE; 34685b9c547cSRui Paulo kay->macsec_validate = Disabled; 34695b9c547cSRui Paulo kay->macsec_replay_protect = FALSE; 34705b9c547cSRui Paulo kay->macsec_replay_window = 0; 34715b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_NONE; 3472*4bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME; 34735b9c547cSRui Paulo } else { 34745b9c547cSRui Paulo kay->macsec_desired = TRUE; 34755b9c547cSRui Paulo kay->macsec_protect = TRUE; 3476*4bc52338SCy Schubert if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF && 3477*4bc52338SCy Schubert policy == SHOULD_ENCRYPT) { 3478*4bc52338SCy Schubert kay->macsec_encrypt = TRUE; 34795b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; 3480*4bc52338SCy Schubert } else { /* SHOULD_SECURE */ 3481*4bc52338SCy Schubert kay->macsec_encrypt = FALSE; 348285732ac8SCy Schubert kay->macsec_confidentiality = CONFIDENTIALITY_NONE; 34835b9c547cSRui Paulo } 3484*4bc52338SCy Schubert kay->macsec_validate = Strict; 3485*4bc52338SCy Schubert kay->macsec_replay_protect = macsec_replay_protect; 3486*4bc52338SCy Schubert kay->macsec_replay_window = macsec_replay_window; 3487*4bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME; 3488*4bc52338SCy Schubert } 34895b9c547cSRui Paulo 34905b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine created"); 34915b9c547cSRui Paulo 34925b9c547cSRui Paulo /* Initialize the SecY must be prio to CP, as CP will control SecY */ 349385732ac8SCy Schubert if (secy_init_macsec(kay) < 0) { 349485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec"); 349585732ac8SCy Schubert goto error; 349685732ac8SCy Schubert } 34975b9c547cSRui Paulo 34985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); 34995b9c547cSRui Paulo 35005b9c547cSRui Paulo /* init CP */ 3501780fb4a2SCy Schubert kay->cp = ieee802_1x_cp_sm_init(kay); 350285732ac8SCy Schubert if (kay->cp == NULL) 350385732ac8SCy Schubert goto error; 35045b9c547cSRui Paulo 35055b9c547cSRui Paulo if (policy == DO_NOT_SECURE) { 35065b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp); 35075b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 35085b9c547cSRui Paulo } else { 35095b9c547cSRui Paulo kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE, 35105b9c547cSRui Paulo kay_l2_receive, kay, 1); 35115b9c547cSRui Paulo if (kay->l2_mka == NULL) { 35125b9c547cSRui Paulo wpa_printf(MSG_WARNING, 35135b9c547cSRui Paulo "KaY: Failed to initialize L2 packet processing for MKA packet"); 351485732ac8SCy Schubert goto error; 35155b9c547cSRui Paulo } 35165b9c547cSRui Paulo } 35175b9c547cSRui Paulo 35185b9c547cSRui Paulo return kay; 351985732ac8SCy Schubert 352085732ac8SCy Schubert error: 352185732ac8SCy Schubert ieee802_1x_kay_deinit(kay); 352285732ac8SCy Schubert return NULL; 35235b9c547cSRui Paulo } 35245b9c547cSRui Paulo 35255b9c547cSRui Paulo 35265b9c547cSRui Paulo /** 35275b9c547cSRui Paulo * ieee802_1x_kay_deinit - 35285b9c547cSRui Paulo */ 35295b9c547cSRui Paulo void 35305b9c547cSRui Paulo ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) 35315b9c547cSRui Paulo { 35325b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 35335b9c547cSRui Paulo 35345b9c547cSRui Paulo if (!kay) 35355b9c547cSRui Paulo return; 35365b9c547cSRui Paulo 35375b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine removed"); 35385b9c547cSRui Paulo 35395b9c547cSRui Paulo while (!dl_list_empty(&kay->participant_list)) { 35405b9c547cSRui Paulo participant = dl_list_entry(kay->participant_list.next, 35415b9c547cSRui Paulo struct ieee802_1x_mka_participant, 35425b9c547cSRui Paulo list); 35435b9c547cSRui Paulo ieee802_1x_kay_delete_mka(kay, &participant->ckn); 35445b9c547cSRui Paulo } 35455b9c547cSRui Paulo 35465b9c547cSRui Paulo ieee802_1x_cp_sm_deinit(kay->cp); 35475b9c547cSRui Paulo secy_deinit_macsec(kay); 35485b9c547cSRui Paulo 35495b9c547cSRui Paulo if (kay->l2_mka) { 35505b9c547cSRui Paulo l2_packet_deinit(kay->l2_mka); 35515b9c547cSRui Paulo kay->l2_mka = NULL; 35525b9c547cSRui Paulo } 35535b9c547cSRui Paulo 35545b9c547cSRui Paulo os_free(kay->ctx); 35555b9c547cSRui Paulo os_free(kay); 35565b9c547cSRui Paulo } 35575b9c547cSRui Paulo 35585b9c547cSRui Paulo 3559*4bc52338SCy Schubert static const char * mode_txt(enum mka_created_mode mode) 3560*4bc52338SCy Schubert { 3561*4bc52338SCy Schubert switch (mode) { 3562*4bc52338SCy Schubert case PSK: 3563*4bc52338SCy Schubert return "PSK"; 3564*4bc52338SCy Schubert case EAP_EXCHANGE: 3565*4bc52338SCy Schubert return "EAP"; 3566*4bc52338SCy Schubert } 3567*4bc52338SCy Schubert 3568*4bc52338SCy Schubert return "?"; 3569*4bc52338SCy Schubert } 3570*4bc52338SCy Schubert 3571*4bc52338SCy Schubert 35725b9c547cSRui Paulo /** 35735b9c547cSRui Paulo * ieee802_1x_kay_create_mka - 35745b9c547cSRui Paulo */ 35755b9c547cSRui Paulo struct ieee802_1x_mka_participant * 357685732ac8SCy Schubert ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, 357785732ac8SCy Schubert const struct mka_key_name *ckn, 357885732ac8SCy Schubert const struct mka_key *cak, u32 life, 35795b9c547cSRui Paulo enum mka_created_mode mode, Boolean is_authenticator) 35805b9c547cSRui Paulo { 35815b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 35825b9c547cSRui Paulo unsigned int usecs; 35835b9c547cSRui Paulo 3584*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 3585*4bc52338SCy Schubert "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)", 3586*4bc52338SCy Schubert kay->if_name, mode_txt(mode), yes_no(is_authenticator)); 3587*4bc52338SCy Schubert 35885b9c547cSRui Paulo if (!kay || !ckn || !cak) { 35895b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: ckn or cak is null"); 35905b9c547cSRui Paulo return NULL; 35915b9c547cSRui Paulo } 35925b9c547cSRui Paulo 3593*4bc52338SCy Schubert if (cak->len != 16 && cak->len != 32) { 3594*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u", 3595*4bc52338SCy Schubert (unsigned int) cak->len); 35965b9c547cSRui Paulo return NULL; 35975b9c547cSRui Paulo } 35985b9c547cSRui Paulo if (ckn->len > MAX_CKN_LEN) { 3599*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)"); 36005b9c547cSRui Paulo return NULL; 36015b9c547cSRui Paulo } 36025b9c547cSRui Paulo if (!kay->enable) { 36035b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: Now is at disable state"); 36045b9c547cSRui Paulo return NULL; 36055b9c547cSRui Paulo } 36065b9c547cSRui Paulo 36075b9c547cSRui Paulo participant = os_zalloc(sizeof(*participant)); 36085b9c547cSRui Paulo if (!participant) { 36095b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); 36105b9c547cSRui Paulo return NULL; 36115b9c547cSRui Paulo } 36125b9c547cSRui Paulo 36135b9c547cSRui Paulo participant->ckn.len = ckn->len; 36145b9c547cSRui Paulo os_memcpy(participant->ckn.name, ckn->name, ckn->len); 3615*4bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name, 3616*4bc52338SCy Schubert participant->ckn.len); 36175b9c547cSRui Paulo participant->cak.len = cak->len; 36185b9c547cSRui Paulo os_memcpy(participant->cak.key, cak->key, cak->len); 3619*4bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key, 3620*4bc52338SCy Schubert participant->cak.len); 36215b9c547cSRui Paulo if (life) 36225b9c547cSRui Paulo participant->cak_life = life + time(NULL); 36235b9c547cSRui Paulo 36245b9c547cSRui Paulo switch (mode) { 36255b9c547cSRui Paulo case EAP_EXCHANGE: 36265b9c547cSRui Paulo if (is_authenticator) { 36275b9c547cSRui Paulo participant->is_obliged_key_server = TRUE; 36285b9c547cSRui Paulo participant->can_be_key_server = TRUE; 36295b9c547cSRui Paulo participant->is_key_server = TRUE; 36305b9c547cSRui Paulo participant->principal = TRUE; 36315b9c547cSRui Paulo 36325b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci, 36335b9c547cSRui Paulo sizeof(kay->key_server_sci)); 36345b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority; 36355b9c547cSRui Paulo participant->is_elected = TRUE; 36365b9c547cSRui Paulo } else { 36375b9c547cSRui Paulo participant->is_obliged_key_server = FALSE; 36385b9c547cSRui Paulo participant->can_be_key_server = FALSE; 36395b9c547cSRui Paulo participant->is_key_server = FALSE; 36405b9c547cSRui Paulo participant->is_elected = TRUE; 36415b9c547cSRui Paulo } 36425b9c547cSRui Paulo break; 36435b9c547cSRui Paulo 36445b9c547cSRui Paulo default: 36455b9c547cSRui Paulo participant->is_obliged_key_server = FALSE; 36465b9c547cSRui Paulo participant->can_be_key_server = TRUE; 3647780fb4a2SCy Schubert participant->is_key_server = TRUE; 36485b9c547cSRui Paulo participant->is_elected = FALSE; 36495b9c547cSRui Paulo break; 36505b9c547cSRui Paulo } 36515b9c547cSRui Paulo 36525b9c547cSRui Paulo participant->cached = FALSE; 36535b9c547cSRui Paulo 36545b9c547cSRui Paulo participant->active = FALSE; 36555b9c547cSRui Paulo participant->participant = FALSE; 36565b9c547cSRui Paulo participant->retain = FALSE; 36575b9c547cSRui Paulo participant->activate = DEFAULT; 36585b9c547cSRui Paulo 36595b9c547cSRui Paulo if (participant->is_key_server) 36605b9c547cSRui Paulo participant->principal = TRUE; 36615b9c547cSRui Paulo 36625b9c547cSRui Paulo dl_list_init(&participant->live_peers); 36635b9c547cSRui Paulo dl_list_init(&participant->potential_peers); 36645b9c547cSRui Paulo 36655b9c547cSRui Paulo participant->retry_count = 0; 36665b9c547cSRui Paulo participant->kay = kay; 36675b9c547cSRui Paulo 3668780fb4a2SCy Schubert if (!reset_participant_mi(participant)) 36695b9c547cSRui Paulo goto fail; 3670*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s", 3671*4bc52338SCy Schubert mi_txt(participant->mi)); 36725b9c547cSRui Paulo 36735b9c547cSRui Paulo participant->lrx = FALSE; 36745b9c547cSRui Paulo participant->ltx = FALSE; 36755b9c547cSRui Paulo participant->orx = FALSE; 36765b9c547cSRui Paulo participant->otx = FALSE; 36775b9c547cSRui Paulo participant->to_dist_sak = FALSE; 36785b9c547cSRui Paulo participant->to_use_sak = FALSE; 36795b9c547cSRui Paulo participant->new_sak = FALSE; 36805b9c547cSRui Paulo dl_list_init(&participant->sak_list); 36815b9c547cSRui Paulo participant->new_key = NULL; 36825b9c547cSRui Paulo dl_list_init(&participant->rxsc_list); 368385732ac8SCy Schubert participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci); 36845b9c547cSRui Paulo secy_cp_control_protect_frames(kay, kay->macsec_protect); 36855b9c547cSRui Paulo secy_cp_control_replay(kay, kay->macsec_replay_protect, 36865b9c547cSRui Paulo kay->macsec_replay_window); 3687*4bc52338SCy Schubert if (secy_create_transmit_sc(kay, participant->txsc)) 3688*4bc52338SCy Schubert goto fail; 36895b9c547cSRui Paulo 36905b9c547cSRui Paulo /* to derive KEK from CAK and CKN */ 3691*4bc52338SCy Schubert participant->kek.len = participant->cak.len; 36925b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key, 3693*4bc52338SCy Schubert participant->cak.len, 36945b9c547cSRui Paulo participant->ckn.name, 36955b9c547cSRui Paulo participant->ckn.len, 3696*4bc52338SCy Schubert participant->kek.key, 3697*4bc52338SCy Schubert participant->kek.len)) { 3698*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: KEK derivation failed"); 36995b9c547cSRui Paulo goto fail; 37005b9c547cSRui Paulo } 37015b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK", 37025b9c547cSRui Paulo participant->kek.key, participant->kek.len); 37035b9c547cSRui Paulo 37045b9c547cSRui Paulo /* to derive ICK from CAK and CKN */ 3705*4bc52338SCy Schubert participant->ick.len = participant->cak.len; 37065b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key, 3707*4bc52338SCy Schubert participant->cak.len, 37085b9c547cSRui Paulo participant->ckn.name, 37095b9c547cSRui Paulo participant->ckn.len, 3710*4bc52338SCy Schubert participant->ick.key, 3711*4bc52338SCy Schubert participant->ick.len)) { 3712*4bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: ICK derivation failed"); 37135b9c547cSRui Paulo goto fail; 37145b9c547cSRui Paulo } 37155b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK", 37165b9c547cSRui Paulo participant->ick.key, participant->ick.len); 37175b9c547cSRui Paulo 37185b9c547cSRui Paulo dl_list_add(&kay->participant_list, &participant->list); 37195b9c547cSRui Paulo 3720*4bc52338SCy Schubert usecs = os_random() % (kay->mka_hello_time * 1000); 37215b9c547cSRui Paulo eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, 37225b9c547cSRui Paulo participant, NULL); 372385732ac8SCy Schubert 372485732ac8SCy Schubert /* Disable MKA lifetime for PSK mode. 372585732ac8SCy Schubert * The peer(s) can take a long time to come up, because we 372685732ac8SCy Schubert * create a "standby" MKA, and we need it to remain live until 372785732ac8SCy Schubert * some peer appears. 372885732ac8SCy Schubert */ 372985732ac8SCy Schubert if (mode != PSK) { 37305b9c547cSRui Paulo participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + 37315b9c547cSRui Paulo usecs / 1000000; 373285732ac8SCy Schubert } 373385732ac8SCy Schubert participant->mode = mode; 37345b9c547cSRui Paulo 37355b9c547cSRui Paulo return participant; 37365b9c547cSRui Paulo 37375b9c547cSRui Paulo fail: 3738*4bc52338SCy Schubert os_free(participant->txsc); 37395b9c547cSRui Paulo os_free(participant); 37405b9c547cSRui Paulo return NULL; 37415b9c547cSRui Paulo } 37425b9c547cSRui Paulo 37435b9c547cSRui Paulo 37445b9c547cSRui Paulo /** 37455b9c547cSRui Paulo * ieee802_1x_kay_delete_mka - 37465b9c547cSRui Paulo */ 37475b9c547cSRui Paulo void 37485b9c547cSRui Paulo ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) 37495b9c547cSRui Paulo { 37505b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 37515b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 37525b9c547cSRui Paulo struct data_key *sak; 37535b9c547cSRui Paulo struct receive_sc *rxsc; 37545b9c547cSRui Paulo 37555b9c547cSRui Paulo if (!kay || !ckn) 37565b9c547cSRui Paulo return; 37575b9c547cSRui Paulo 37585b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: participant removed"); 37595b9c547cSRui Paulo 37605b9c547cSRui Paulo /* get the participant */ 376185732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); 37625b9c547cSRui Paulo if (!participant) { 37635b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "KaY: participant is not found", 37645b9c547cSRui Paulo ckn->name, ckn->len); 37655b9c547cSRui Paulo return; 37665b9c547cSRui Paulo } 37675b9c547cSRui Paulo 3768780fb4a2SCy Schubert eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL); 37695b9c547cSRui Paulo dl_list_del(&participant->list); 37705b9c547cSRui Paulo 37715b9c547cSRui Paulo /* remove live peer */ 37725b9c547cSRui Paulo while (!dl_list_empty(&participant->live_peers)) { 37735b9c547cSRui Paulo peer = dl_list_entry(participant->live_peers.next, 37745b9c547cSRui Paulo struct ieee802_1x_kay_peer, list); 37755b9c547cSRui Paulo dl_list_del(&peer->list); 37765b9c547cSRui Paulo os_free(peer); 37775b9c547cSRui Paulo } 37785b9c547cSRui Paulo 37795b9c547cSRui Paulo /* remove potential peer */ 37805b9c547cSRui Paulo while (!dl_list_empty(&participant->potential_peers)) { 37815b9c547cSRui Paulo peer = dl_list_entry(participant->potential_peers.next, 37825b9c547cSRui Paulo struct ieee802_1x_kay_peer, list); 37835b9c547cSRui Paulo dl_list_del(&peer->list); 37845b9c547cSRui Paulo os_free(peer); 37855b9c547cSRui Paulo } 37865b9c547cSRui Paulo 37875b9c547cSRui Paulo /* remove sak */ 37885b9c547cSRui Paulo while (!dl_list_empty(&participant->sak_list)) { 37895b9c547cSRui Paulo sak = dl_list_entry(participant->sak_list.next, 37905b9c547cSRui Paulo struct data_key, list); 37915b9c547cSRui Paulo dl_list_del(&sak->list); 379285732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(sak); 37935b9c547cSRui Paulo } 37945b9c547cSRui Paulo while (!dl_list_empty(&participant->rxsc_list)) { 37955b9c547cSRui Paulo rxsc = dl_list_entry(participant->rxsc_list.next, 37965b9c547cSRui Paulo struct receive_sc, list); 37975b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(participant, rxsc); 37985b9c547cSRui Paulo } 37995b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); 38005b9c547cSRui Paulo 38015b9c547cSRui Paulo os_memset(&participant->cak, 0, sizeof(participant->cak)); 38025b9c547cSRui Paulo os_memset(&participant->kek, 0, sizeof(participant->kek)); 38035b9c547cSRui Paulo os_memset(&participant->ick, 0, sizeof(participant->ick)); 38045b9c547cSRui Paulo os_free(participant); 38055b9c547cSRui Paulo } 38065b9c547cSRui Paulo 38075b9c547cSRui Paulo 38085b9c547cSRui Paulo /** 38095b9c547cSRui Paulo * ieee802_1x_kay_mka_participate - 38105b9c547cSRui Paulo */ 38115b9c547cSRui Paulo void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, 38125b9c547cSRui Paulo struct mka_key_name *ckn, 38135b9c547cSRui Paulo Boolean status) 38145b9c547cSRui Paulo { 38155b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 38165b9c547cSRui Paulo 38175b9c547cSRui Paulo if (!kay || !ckn) 38185b9c547cSRui Paulo return; 38195b9c547cSRui Paulo 382085732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); 38215b9c547cSRui Paulo if (!participant) 38225b9c547cSRui Paulo return; 38235b9c547cSRui Paulo 38245b9c547cSRui Paulo participant->active = status; 38255b9c547cSRui Paulo } 38265b9c547cSRui Paulo 38275b9c547cSRui Paulo 38285b9c547cSRui Paulo /** 38295b9c547cSRui Paulo * ieee802_1x_kay_new_sak - 38305b9c547cSRui Paulo */ 38315b9c547cSRui Paulo int 38325b9c547cSRui Paulo ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay) 38335b9c547cSRui Paulo { 38345b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 38355b9c547cSRui Paulo 38365b9c547cSRui Paulo if (!kay) 38375b9c547cSRui Paulo return -1; 38385b9c547cSRui Paulo 38395b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay); 38405b9c547cSRui Paulo if (!participant) 38415b9c547cSRui Paulo return -1; 38425b9c547cSRui Paulo 38435b9c547cSRui Paulo participant->new_sak = TRUE; 38445b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: new SAK signal"); 38455b9c547cSRui Paulo 38465b9c547cSRui Paulo return 0; 38475b9c547cSRui Paulo } 38485b9c547cSRui Paulo 38495b9c547cSRui Paulo 38505b9c547cSRui Paulo /** 38515b9c547cSRui Paulo * ieee802_1x_kay_change_cipher_suite - 38525b9c547cSRui Paulo */ 38535b9c547cSRui Paulo int 3854780fb4a2SCy Schubert ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, 3855780fb4a2SCy Schubert unsigned int cs_index) 38565b9c547cSRui Paulo { 38575b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 385885732ac8SCy Schubert enum macsec_cap secy_cap; 38595b9c547cSRui Paulo 38605b9c547cSRui Paulo if (!kay) 38615b9c547cSRui Paulo return -1; 38625b9c547cSRui Paulo 3863780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE) { 38645b9c547cSRui Paulo wpa_printf(MSG_ERROR, 38655b9c547cSRui Paulo "KaY: Configured cipher suite index is out of range"); 38665b9c547cSRui Paulo return -1; 38675b9c547cSRui Paulo } 38685b9c547cSRui Paulo if (kay->macsec_csindex == cs_index) 38695b9c547cSRui Paulo return -2; 38705b9c547cSRui Paulo 38715b9c547cSRui Paulo if (cs_index == 0) 38725b9c547cSRui Paulo kay->macsec_desired = FALSE; 38735b9c547cSRui Paulo 38745b9c547cSRui Paulo kay->macsec_csindex = cs_index; 38755b9c547cSRui Paulo kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable; 38765b9c547cSRui Paulo 387785732ac8SCy Schubert if (secy_get_capability(kay, &secy_cap) < 0) 387885732ac8SCy Schubert return -3; 387985732ac8SCy Schubert 388085732ac8SCy Schubert if (kay->macsec_capable > secy_cap) 388185732ac8SCy Schubert kay->macsec_capable = secy_cap; 388285732ac8SCy Schubert 38835b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay); 38845b9c547cSRui Paulo if (participant) { 38855b9c547cSRui Paulo wpa_printf(MSG_INFO, "KaY: Cipher Suite changed"); 38865b9c547cSRui Paulo participant->new_sak = TRUE; 38875b9c547cSRui Paulo } 38885b9c547cSRui Paulo 38895b9c547cSRui Paulo return 0; 38905b9c547cSRui Paulo } 389185732ac8SCy Schubert 389285732ac8SCy Schubert 389385732ac8SCy Schubert #ifdef CONFIG_CTRL_IFACE 3894*4bc52338SCy Schubert 389585732ac8SCy Schubert /** 389685732ac8SCy Schubert * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details 389785732ac8SCy Schubert * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() 389885732ac8SCy Schubert * @buf: Buffer for status information 389985732ac8SCy Schubert * @buflen: Maximum buffer length 390085732ac8SCy Schubert * @verbose: Whether to include verbose status information 390185732ac8SCy Schubert * Returns: Number of bytes written to buf. 390285732ac8SCy Schubert * 3903*4bc52338SCy Schubert * Query KaY status information. This function fills in a text area with current 390485732ac8SCy Schubert * status information. If the buffer (buf) is not large enough, status 390585732ac8SCy Schubert * information will be truncated to fit the buffer. 390685732ac8SCy Schubert */ 390785732ac8SCy Schubert int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, 390885732ac8SCy Schubert size_t buflen) 390985732ac8SCy Schubert { 3910*4bc52338SCy Schubert char *pos, *end; 3911*4bc52338SCy Schubert int res, count; 3912*4bc52338SCy Schubert struct ieee802_1x_mka_participant *p; 391385732ac8SCy Schubert 391485732ac8SCy Schubert if (!kay) 391585732ac8SCy Schubert return 0; 391685732ac8SCy Schubert 3917*4bc52338SCy Schubert pos = buf; 3918*4bc52338SCy Schubert end = buf + buflen; 3919*4bc52338SCy Schubert 3920*4bc52338SCy Schubert res = os_snprintf(pos, end - pos, 392185732ac8SCy Schubert "PAE KaY status=%s\n" 392285732ac8SCy Schubert "Authenticated=%s\n" 392385732ac8SCy Schubert "Secured=%s\n" 392485732ac8SCy Schubert "Failed=%s\n" 392585732ac8SCy Schubert "Actor Priority=%u\n" 392685732ac8SCy Schubert "Key Server Priority=%u\n" 392785732ac8SCy Schubert "Is Key Server=%s\n" 392885732ac8SCy Schubert "Number of Keys Distributed=%u\n" 3929*4bc52338SCy Schubert "Number of Keys Received=%u\n" 3930*4bc52338SCy Schubert "MKA Hello Time=%u\n", 393185732ac8SCy Schubert kay->active ? "Active" : "Not-Active", 393285732ac8SCy Schubert kay->authenticated ? "Yes" : "No", 393385732ac8SCy Schubert kay->secured ? "Yes" : "No", 393485732ac8SCy Schubert kay->failed ? "Yes" : "No", 393585732ac8SCy Schubert kay->actor_priority, 393685732ac8SCy Schubert kay->key_server_priority, 393785732ac8SCy Schubert kay->is_key_server ? "Yes" : "No", 393885732ac8SCy Schubert kay->dist_kn - 1, 3939*4bc52338SCy Schubert kay->rcvd_keys, 3940*4bc52338SCy Schubert kay->mka_hello_time); 3941*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 3942*4bc52338SCy Schubert return 0; 3943*4bc52338SCy Schubert pos += res; 3944*4bc52338SCy Schubert 3945*4bc52338SCy Schubert res = os_snprintf(pos, end - pos, 3946*4bc52338SCy Schubert "actor_sci=%s\n", sci_txt(&kay->actor_sci)); 3947*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 3948*4bc52338SCy Schubert return end - pos; 3949*4bc52338SCy Schubert pos += res; 3950*4bc52338SCy Schubert 3951*4bc52338SCy Schubert res = os_snprintf(pos, end - pos, 3952*4bc52338SCy Schubert "key_server_sci=%s\n", sci_txt(&kay->key_server_sci)); 3953*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 3954*4bc52338SCy Schubert return end - pos; 3955*4bc52338SCy Schubert pos += res; 3956*4bc52338SCy Schubert 3957*4bc52338SCy Schubert count = 0; 3958*4bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list, 3959*4bc52338SCy Schubert struct ieee802_1x_mka_participant, list) { 3960*4bc52338SCy Schubert char *pos2 = pos; 3961*4bc52338SCy Schubert 3962*4bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=", 3963*4bc52338SCy Schubert count); 3964*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 3965*4bc52338SCy Schubert return end - pos; 3966*4bc52338SCy Schubert pos2 += res; 3967*4bc52338SCy Schubert count++; 3968*4bc52338SCy Schubert 3969*4bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, 3970*4bc52338SCy Schubert p->ckn.len); 3971*4bc52338SCy Schubert 3972*4bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, 3973*4bc52338SCy Schubert "\nmi=%s\n" 3974*4bc52338SCy Schubert "mn=%u\n" 3975*4bc52338SCy Schubert "active=%s\n" 3976*4bc52338SCy Schubert "participant=%s\n" 3977*4bc52338SCy Schubert "retain=%s\n" 3978*4bc52338SCy Schubert "live_peers=%u\n" 3979*4bc52338SCy Schubert "potential_peers=%u\n" 3980*4bc52338SCy Schubert "is_key_server=%s\n" 3981*4bc52338SCy Schubert "is_elected=%s\n", 3982*4bc52338SCy Schubert mi_txt(p->mi), p->mn, 3983*4bc52338SCy Schubert yes_no(p->active), 3984*4bc52338SCy Schubert yes_no(p->participant), 3985*4bc52338SCy Schubert yes_no(p->retain), 3986*4bc52338SCy Schubert dl_list_len(&p->live_peers), 3987*4bc52338SCy Schubert dl_list_len(&p->potential_peers), 3988*4bc52338SCy Schubert yes_no(p->is_key_server), 3989*4bc52338SCy Schubert yes_no(p->is_elected)); 3990*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 3991*4bc52338SCy Schubert return end - pos; 3992*4bc52338SCy Schubert pos2 += res; 3993*4bc52338SCy Schubert pos = pos2; 3994*4bc52338SCy Schubert } 3995*4bc52338SCy Schubert 3996*4bc52338SCy Schubert return pos - buf; 3997*4bc52338SCy Schubert } 3998*4bc52338SCy Schubert 3999*4bc52338SCy Schubert 4000*4bc52338SCy Schubert static const char * true_false(Boolean val) 4001*4bc52338SCy Schubert { 4002*4bc52338SCy Schubert return val ? "true" : "false"; 4003*4bc52338SCy Schubert } 4004*4bc52338SCy Schubert 4005*4bc52338SCy Schubert 4006*4bc52338SCy Schubert static const char * activate_control_txt(enum activate_ctrl activate) 4007*4bc52338SCy Schubert { 4008*4bc52338SCy Schubert switch (activate) { 4009*4bc52338SCy Schubert case DEFAULT: 4010*4bc52338SCy Schubert return "default"; 4011*4bc52338SCy Schubert case DISABLED: 4012*4bc52338SCy Schubert return "disabled"; 4013*4bc52338SCy Schubert case ON_OPER_UP: 4014*4bc52338SCy Schubert return "onOperUp"; 4015*4bc52338SCy Schubert case ALWAYS: 4016*4bc52338SCy Schubert return "always"; 4017*4bc52338SCy Schubert } 4018*4bc52338SCy Schubert 4019*4bc52338SCy Schubert return "?"; 4020*4bc52338SCy Schubert } 4021*4bc52338SCy Schubert 4022*4bc52338SCy Schubert 4023*4bc52338SCy Schubert static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf, 4024*4bc52338SCy Schubert char *end) 4025*4bc52338SCy Schubert { 4026*4bc52338SCy Schubert char *pos = buf; 4027*4bc52338SCy Schubert struct ieee802_1x_kay_peer *p; 4028*4bc52338SCy Schubert int res; 4029*4bc52338SCy Schubert 4030*4bc52338SCy Schubert dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) { 4031*4bc52338SCy Schubert res = os_snprintf(pos, end - pos, 4032*4bc52338SCy Schubert "ieee8021XKayMkaPeerListMI=%s\n" 4033*4bc52338SCy Schubert "ieee8021XKayMkaPeerListMN=%u\n" 4034*4bc52338SCy Schubert "ieee8021XKayMkaPeerListType=%u\n" 4035*4bc52338SCy Schubert "ieee8021XKayMkaPeerListSCI=%s\n", 4036*4bc52338SCy Schubert mi_txt(p->mi), 4037*4bc52338SCy Schubert p->mn, 4038*4bc52338SCy Schubert live ? 1 : 2, 4039*4bc52338SCy Schubert sci_txt(&p->sci)); 4040*4bc52338SCy Schubert if (os_snprintf_error(end - pos, res)) 4041*4bc52338SCy Schubert return pos; 4042*4bc52338SCy Schubert pos += res; 4043*4bc52338SCy Schubert } 4044*4bc52338SCy Schubert 4045*4bc52338SCy Schubert return pos; 4046*4bc52338SCy Schubert } 4047*4bc52338SCy Schubert 4048*4bc52338SCy Schubert 4049*4bc52338SCy Schubert int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, 4050*4bc52338SCy Schubert size_t buflen) 4051*4bc52338SCy Schubert { 4052*4bc52338SCy Schubert char *pos, *end; 4053*4bc52338SCy Schubert int res; 4054*4bc52338SCy Schubert struct ieee802_1x_mka_participant *p; 4055*4bc52338SCy Schubert 4056*4bc52338SCy Schubert if (!kay) 405785732ac8SCy Schubert return 0; 405885732ac8SCy Schubert 4059*4bc52338SCy Schubert pos = buf; 4060*4bc52338SCy Schubert end = buf + buflen; 4061*4bc52338SCy Schubert 4062*4bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list, 4063*4bc52338SCy Schubert struct ieee802_1x_mka_participant, list) { 4064*4bc52338SCy Schubert char *pos2 = pos; 4065*4bc52338SCy Schubert 4066*4bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN="); 4067*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 4068*4bc52338SCy Schubert return end - pos; 4069*4bc52338SCy Schubert pos2 += res; 4070*4bc52338SCy Schubert 4071*4bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, 4072*4bc52338SCy Schubert p->ckn.len); 4073*4bc52338SCy Schubert 4074*4bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, 4075*4bc52338SCy Schubert "\nieee8021XKayMkaPartCached=%s\n" 4076*4bc52338SCy Schubert "ieee8021XKayMkaPartActive=%s\n" 4077*4bc52338SCy Schubert "ieee8021XKayMkaPartRetain=%s\n" 4078*4bc52338SCy Schubert "ieee8021XKayMkaPartActivateControl=%s\n" 4079*4bc52338SCy Schubert "ieee8021XKayMkaPartPrincipal=%s\n", 4080*4bc52338SCy Schubert true_false(p->cached), 4081*4bc52338SCy Schubert true_false(p->active), 4082*4bc52338SCy Schubert true_false(p->retain), 4083*4bc52338SCy Schubert activate_control_txt(p->activate), 4084*4bc52338SCy Schubert true_false(p->principal)); 4085*4bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 4086*4bc52338SCy Schubert return end - pos; 4087*4bc52338SCy Schubert pos2 += res; 4088*4bc52338SCy Schubert pos = pos2; 4089*4bc52338SCy Schubert 4090*4bc52338SCy Schubert pos = mka_mib_peer(&p->live_peers, TRUE, pos, end); 4091*4bc52338SCy Schubert pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end); 409285732ac8SCy Schubert } 4093*4bc52338SCy Schubert 4094*4bc52338SCy Schubert return pos - buf; 4095*4bc52338SCy Schubert } 4096*4bc52338SCy Schubert 409785732ac8SCy Schubert #endif /* CONFIG_CTRL_IFACE */ 4098