15b9c547cSRui Paulo /* 24bc52338SCy 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 304bc52338SCy Schubert #define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without 314bc52338SCy Schubert * SAK-USE before dropping */ 324bc52338SCy 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 674bc52338SCy Schubert .cak_trfm = ieee802_1x_cak_aes_cmac, 684bc52338SCy Schubert .ckn_trfm = ieee802_1x_ckn_aes_cmac, 694bc52338SCy Schubert .kek_trfm = ieee802_1x_kek_aes_cmac, 704bc52338SCy Schubert .ick_trfm = ieee802_1x_ick_aes_cmac, 714bc52338SCy 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 1074bc52338SCy Schubert static const char * mi_txt(const u8 *mi) 1084bc52338SCy Schubert { 1094bc52338SCy Schubert static char txt[MI_LEN * 2 + 1]; 1104bc52338SCy Schubert 1114bc52338SCy Schubert wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN); 1124bc52338SCy Schubert return txt; 1134bc52338SCy Schubert } 1144bc52338SCy Schubert 1154bc52338SCy Schubert 1164bc52338SCy Schubert static const char * sci_txt(const struct ieee802_1x_mka_sci *sci) 1174bc52338SCy Schubert { 1184bc52338SCy Schubert static char txt[ETH_ALEN * 3 + 1 + 5 + 1]; 1194bc52338SCy Schubert 1204bc52338SCy Schubert os_snprintf(txt, sizeof(txt), MACSTR "@%u", 1214bc52338SCy Schubert MAC2STR(sci->addr), be_to_host16(sci->port)); 1224bc52338SCy Schubert return txt; 1234bc52338SCy Schubert } 1244bc52338SCy Schubert 1254bc52338SCy Schubert 1264bc52338SCy Schubert static const char * algo_agility_txt(const u8 *algo_agility) 1274bc52338SCy Schubert { 1284bc52338SCy Schubert static char txt[4 * 2 + 1]; 1294bc52338SCy Schubert 1304bc52338SCy Schubert wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4); 1314bc52338SCy Schubert return txt; 1324bc52338SCy Schubert } 1334bc52338SCy Schubert 1344bc52338SCy 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 1464bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-8 */ 1475b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 1484bc52338SCy Schubert wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set"); 1494bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version); 1504bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority); 1514bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server); 1524bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired); 1534bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d", 1544bc52338SCy Schubert body->macsec_capability); 1554bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len); 1564bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci)); 1574bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s", 1584bc52338SCy Schubert mi_txt(body->actor_mi)); 1594bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d", 1605b9c547cSRui Paulo be_to_host32(body->actor_mn)); 1614bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s", 1624bc52338SCy Schubert algo_agility_txt(body->algo_agility)); 1634bc52338SCy 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 1824bc52338SCy 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) { 1854bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Live Peer List parameter set"); 1864bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); 1875b9c547cSRui Paulo } else if (body->type == MKA_POTENTIAL_PEER_LIST) { 1884bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Potential Peer List parameter set"); 1894bc52338SCy 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)); 1954bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d", 1964bc52338SCy 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 2124bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */ 2135b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 2144bc52338SCy Schubert wpa_printf(MSG_DEBUG, "Distributed SAK parameter set"); 2154bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan); 2164bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d", 2175b9c547cSRui Paulo body->confid_offset); 2184bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len); 2195b9c547cSRui Paulo if (!body_len) 2205b9c547cSRui Paulo return; 2215b9c547cSRui Paulo 2224bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Number............: %d", 2235b9c547cSRui Paulo be_to_host32(body->kn)); 2244bc52338SCy Schubert /* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */ 2254bc52338SCy 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 2464bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-10 */ 2475b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 2484bc52338SCy 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)); 2524bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan); 2534bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx)); 2544bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx)); 2554bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx)); 2564bc52338SCy 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 2634bc52338SCy 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)); 2684bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi)); 2694bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u", 2705b9c547cSRui Paulo be_to_host32(body->okn)); 2714bc52338SCy 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, 3984bc52338SCy 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++) { 4084bc52338SCy Schubert if (cipher_suite_tbl[i].id == cs) { 4094bc52338SCy Schubert *idx = i; 4105b9c547cSRui Paulo return &cipher_suite_tbl[i]; 4115b9c547cSRui Paulo } 4124bc52338SCy 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, 4934bc52338SCy 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); 5404bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s", 5414bc52338SCy 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 { 5784bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s", 5794bc52338SCy 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; 5984bc52338SCy 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 6274bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) { 6284bc52338SCy Schubert os_free(rxsc); 6294bc52338SCy Schubert os_free(peer); 6304bc52338SCy Schubert return NULL; 6314bc52338SCy 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 6574bc52338SCy 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 6874bc52338SCy 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); 6914bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) { 6924bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer"); 6934bc52338SCy Schubert os_free(rxsc); 6944bc52338SCy Schubert os_free(peer); 6954bc52338SCy Schubert return NULL; 6964bc52338SCy 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; 7414bc52338SCy Schubert unsigned int length = sizeof(struct ieee802_1x_mka_basic_body); 7425b9c547cSRui Paulo 7434bc52338SCy Schubert length += participant->ckn.len; 7444bc52338SCy 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; 7484bc52338SCy Schubert /* The Key Server flag is set if and only if the participant has not 7494bc52338SCy 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, 8054bc52338SCy 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) { 8094bc52338SCy 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) { 8234bc52338SCy Schubert wpa_printf(MSG_DEBUG, 8244bc52338SCy 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; 8324bc52338SCy Schubert wpa_printf(MSG_DEBUG, 8334bc52338SCy Schubert "KaY: Peer using my MI - selected a new random MI: %s", 8344bc52338SCy 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) { 8464bc52338SCy Schubert /* Check duplicated SCI 8474bc52338SCy Schubert * 8484bc52338SCy Schubert * A duplicated SCI indicates either an active attacker or 8494bc52338SCy Schubert * a valid peer whose MI is being changed. The latter scenario 8504bc52338SCy Schubert * is more likely because to have gotten this far the received 8514bc52338SCy Schubert * MKPDU must have had a valid ICV, indicating the peer holds 8524bc52338SCy Schubert * the same CAK as our participant. 8534bc52338SCy Schubert * 8544bc52338SCy Schubert * Before creating a new peer object for the new MI we must 8554bc52338SCy Schubert * clean up the resources (SCs and SAs) associated with the 8564bc52338SCy Schubert * old peer. An easy way to do this is to ignore MKPDUs with 8574bc52338SCy Schubert * the new MI's for now and just wait for the old peer to 8584bc52338SCy Schubert * time out and clean itself up (within MKA_LIFE_TIME). 8594bc52338SCy Schubert * 8604bc52338SCy Schubert * This method is preferable to deleting the old peer here 8614bc52338SCy Schubert * and now and continuing on with processing because if this 8624bc52338SCy Schubert * MKPDU is from an attacker it's better to ignore the MKPDU 8634bc52338SCy 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) { 8684bc52338SCy Schubert time_t new_expire; 8694bc52338SCy Schubert 8705b9c547cSRui Paulo wpa_printf(MSG_WARNING, 8714bc52338SCy Schubert "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU"); 8724bc52338SCy Schubert /* Reduce timeout to speed up this process but left the 8734bc52338SCy Schubert * chance for old one to prove aliveness. */ 8744bc52338SCy Schubert new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000; 8754bc52338SCy Schubert if (peer->expire > new_expire) 8764bc52338SCy Schubert peer->expire = new_expire; 8774bc52338SCy 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)); 8834bc52338SCy Schubert if (!peer) { 8844bc52338SCy Schubert wpa_printf(MSG_DEBUG, 8854bc52338SCy Schubert "KaY: No potential peer entry found - ignore MKPDU"); 8865b9c547cSRui Paulo return NULL; 8874bc52338SCy 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 { 9004bc52338SCy Schubert wpa_printf(MSG_WARNING, 9014bc52338SCy 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; 10464bc52338SCy Schubert left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN, 10474bc52338SCy 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, 10824bc52338SCy Schubert MI_LEN) == 0) { 10834bc52338SCy Schubert u32 mn = be_to_host32(peer_mi->mn); 10844bc52338SCy Schubert 10854bc52338SCy Schubert wpa_printf(MSG_DEBUG, 10864bc52338SCy Schubert "KaY: My MI - received MN %u, most recently transmitted MN %u", 10874bc52338SCy Schubert mn, participant->mn); 1088*206b73d0SCy Schubert /* IEEE Std 802.1X-2010 is not exactly clear 1089*206b73d0SCy Schubert * which values of MN should be accepted here. 1090*206b73d0SCy Schubert * It uses "acceptably recent MN" language 1091*206b73d0SCy Schubert * without defining what would be acceptable 1092*206b73d0SCy Schubert * recent. For now, allow the last two used MN 1093*206b73d0SCy Schubert * values (i.e., peer having copied my MI,MN 1094*206b73d0SCy Schubert * from either of the last two MKPDUs that I 1095*206b73d0SCy Schubert * have sent). */ 1096*206b73d0SCy Schubert if (mn == participant->mn || 1097*206b73d0SCy Schubert (participant->mn > 1 && 1098*206b73d0SCy Schubert mn == participant->mn - 1)) 10995b9c547cSRui Paulo return TRUE; 1100780fb4a2SCy Schubert } 11015b9c547cSRui Paulo } 11024bc52338SCy Schubert } 11035b9c547cSRui Paulo 11045b9c547cSRui Paulo return FALSE; 11055b9c547cSRui Paulo } 11065b9c547cSRui Paulo 11075b9c547cSRui Paulo 11085b9c547cSRui Paulo /** 11095b9c547cSRui Paulo * ieee802_1x_mka_decode_live_peer_body - 11105b9c547cSRui Paulo */ 11115b9c547cSRui Paulo static int ieee802_1x_mka_decode_live_peer_body( 11125b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 11135b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len) 11145b9c547cSRui Paulo { 11155b9c547cSRui Paulo const struct ieee802_1x_mka_hdr *hdr; 11165b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 11175b9c547cSRui Paulo size_t body_len; 11185b9c547cSRui Paulo size_t i; 11195b9c547cSRui Paulo Boolean is_included; 11205b9c547cSRui Paulo 11215b9c547cSRui Paulo is_included = ieee802_1x_kay_is_in_live_peer( 11225b9c547cSRui Paulo participant, participant->current_peer_id.mi); 11235b9c547cSRui Paulo 11245b9c547cSRui Paulo hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; 11255b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 1126780fb4a2SCy Schubert if (body_len % 16 != 0) { 1127780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 1128780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", 1129780fb4a2SCy Schubert body_len); 1130780fb4a2SCy Schubert return -1; 1131780fb4a2SCy Schubert } 11325b9c547cSRui Paulo 1133780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { 1134780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi; 1135780fb4a2SCy Schubert u32 peer_mn; 1136780fb4a2SCy Schubert 1137780fb4a2SCy Schubert peer_mi = (const struct ieee802_1x_mka_peer_id *) 1138780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i); 1139780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn); 11405b9c547cSRui Paulo 11415b9c547cSRui Paulo /* it is myself */ 11425b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { 11435b9c547cSRui Paulo /* My message id is used by other participant */ 1144780fb4a2SCy Schubert if (peer_mn > participant->mn && 1145780fb4a2SCy Schubert !reset_participant_mi(participant)) 1146780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 11475b9c547cSRui Paulo continue; 11485b9c547cSRui Paulo } 1149780fb4a2SCy Schubert 11505b9c547cSRui Paulo if (!is_included) 11515b9c547cSRui Paulo continue; 11525b9c547cSRui Paulo 1153780fb4a2SCy Schubert peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi); 1154780fb4a2SCy Schubert if (peer) { 11555b9c547cSRui Paulo peer->mn = peer_mn; 1156780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_potential_peer( 1157780fb4a2SCy Schubert participant, peer_mi->mi, peer_mn)) { 11585b9c547cSRui Paulo return -1; 11595b9c547cSRui Paulo } 11605b9c547cSRui Paulo } 11615b9c547cSRui Paulo 11625b9c547cSRui Paulo return 0; 11635b9c547cSRui Paulo } 11645b9c547cSRui Paulo 11655b9c547cSRui Paulo 11665b9c547cSRui Paulo /** 11675b9c547cSRui Paulo * ieee802_1x_mka_decode_potential_peer_body - 11685b9c547cSRui Paulo */ 11695b9c547cSRui Paulo static int 11705b9c547cSRui Paulo ieee802_1x_mka_decode_potential_peer_body( 11715b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 11725b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len) 11735b9c547cSRui Paulo { 1174780fb4a2SCy Schubert const struct ieee802_1x_mka_hdr *hdr; 11755b9c547cSRui Paulo size_t body_len; 11765b9c547cSRui Paulo size_t i; 11775b9c547cSRui Paulo 1178780fb4a2SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; 11795b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 1180780fb4a2SCy Schubert if (body_len % 16 != 0) { 1181780fb4a2SCy Schubert wpa_printf(MSG_ERROR, 1182780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", 1183780fb4a2SCy Schubert body_len); 1184780fb4a2SCy Schubert return -1; 1185780fb4a2SCy Schubert } 11865b9c547cSRui Paulo 1187780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { 1188780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi; 1189780fb4a2SCy Schubert u32 peer_mn; 1190780fb4a2SCy Schubert 1191780fb4a2SCy Schubert peer_mi = (struct ieee802_1x_mka_peer_id *) 1192780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i); 1193780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn); 11945b9c547cSRui Paulo 11955b9c547cSRui Paulo /* it is myself */ 11965b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { 11975b9c547cSRui Paulo /* My message id is used by other participant */ 1198780fb4a2SCy Schubert if (peer_mn > participant->mn && 1199780fb4a2SCy Schubert !reset_participant_mi(participant)) 1200780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 12015b9c547cSRui Paulo continue; 12025b9c547cSRui Paulo } 12035b9c547cSRui Paulo } 12045b9c547cSRui Paulo 12055b9c547cSRui Paulo return 0; 12065b9c547cSRui Paulo } 12075b9c547cSRui Paulo 12085b9c547cSRui Paulo 12095b9c547cSRui Paulo /** 12105b9c547cSRui Paulo * ieee802_1x_mka_sak_use_body_present 12115b9c547cSRui Paulo */ 12125b9c547cSRui Paulo static Boolean 12135b9c547cSRui Paulo ieee802_1x_mka_sak_use_body_present( 12145b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 12155b9c547cSRui Paulo { 1216780fb4a2SCy Schubert return participant->to_use_sak; 12175b9c547cSRui Paulo } 12185b9c547cSRui Paulo 12195b9c547cSRui Paulo 12205b9c547cSRui Paulo /** 12215b9c547cSRui Paulo * ieee802_1x_mka_get_sak_use_length 12225b9c547cSRui Paulo */ 12235b9c547cSRui Paulo static int 12245b9c547cSRui Paulo ieee802_1x_mka_get_sak_use_length( 12255b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 12265b9c547cSRui Paulo { 12275b9c547cSRui Paulo int length = MKA_HDR_LEN; 12285b9c547cSRui Paulo 12295b9c547cSRui Paulo if (participant->kay->macsec_desired && participant->advised_desired) 12305b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_sak_use_body); 12315b9c547cSRui Paulo 1232780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 12335b9c547cSRui Paulo } 12345b9c547cSRui Paulo 12355b9c547cSRui Paulo 12365b9c547cSRui Paulo /** 12374bc52338SCy Schubert * ieee802_1x_mka_get_lpn 12385b9c547cSRui Paulo */ 12395b9c547cSRui Paulo static u32 12405b9c547cSRui Paulo ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal, 12415b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki) 12425b9c547cSRui Paulo { 12434bc52338SCy Schubert struct transmit_sa *txsa; 12445b9c547cSRui Paulo u32 lpn = 0; 12455b9c547cSRui Paulo 12464bc52338SCy Schubert dl_list_for_each(txsa, &principal->txsc->sa_list, 12474bc52338SCy Schubert struct transmit_sa, list) { 12484bc52338SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { 12494bc52338SCy Schubert /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses 12504bc52338SCy Schubert * MKA to communicate the lowest PN used for 12514bc52338SCy Schubert * transmission with the SAK within the last two 12524bc52338SCy Schubert * seconds". Achieve this 2 second delay by setting the 12534bc52338SCy Schubert * lpn using the transmit next PN (i.e., txsa->next_pn) 12544bc52338SCy Schubert * that was read last time here (i.e., mka_hello_time 12554bc52338SCy Schubert * 2 seconds ago). 12564bc52338SCy Schubert * 12574bc52338SCy Schubert * The lowest acceptable PN is the same as the last 12584bc52338SCy Schubert * transmitted PN, which is one less than the next 12594bc52338SCy Schubert * transmit PN. 12604bc52338SCy Schubert * 12614bc52338SCy Schubert * NOTE: This method only works if mka_hello_time is 2s. 12624bc52338SCy Schubert */ 12634bc52338SCy Schubert lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0; 12645b9c547cSRui Paulo 12654bc52338SCy Schubert /* Now read the current transmit next PN for use next 12664bc52338SCy Schubert * time through. */ 12674bc52338SCy Schubert secy_get_transmit_next_pn(principal->kay, txsa); 12685b9c547cSRui Paulo break; 12695b9c547cSRui Paulo } 12705b9c547cSRui Paulo } 12715b9c547cSRui Paulo 12725b9c547cSRui Paulo if (lpn == 0) 12735b9c547cSRui Paulo lpn = 1; 12745b9c547cSRui Paulo 12755b9c547cSRui Paulo return lpn; 12765b9c547cSRui Paulo } 12775b9c547cSRui Paulo 12785b9c547cSRui Paulo 12795b9c547cSRui Paulo /** 12805b9c547cSRui Paulo * ieee802_1x_mka_encode_sak_use_body - 12815b9c547cSRui Paulo */ 12825b9c547cSRui Paulo static int 12835b9c547cSRui Paulo ieee802_1x_mka_encode_sak_use_body( 12845b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 12855b9c547cSRui Paulo struct wpabuf *buf) 12865b9c547cSRui Paulo { 12875b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body; 1288780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 12895b9c547cSRui Paulo unsigned int length; 12905b9c547cSRui Paulo u32 pn = 1; 12915b9c547cSRui Paulo 12925b9c547cSRui Paulo length = ieee802_1x_mka_get_sak_use_length(participant); 1293780fb4a2SCy Schubert body = wpabuf_put(buf, length); 12945b9c547cSRui Paulo 12955b9c547cSRui Paulo body->type = MKA_SAK_USE; 12965b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 12975b9c547cSRui Paulo 12985b9c547cSRui Paulo if (length == MKA_HDR_LEN) { 12995b9c547cSRui Paulo body->ptx = TRUE; 13005b9c547cSRui Paulo body->prx = TRUE; 13015b9c547cSRui Paulo body->lan = 0; 13025b9c547cSRui Paulo body->lrx = FALSE; 13035b9c547cSRui Paulo body->ltx = FALSE; 13045b9c547cSRui Paulo body->delay_protect = FALSE; 13055b9c547cSRui Paulo return 0; 13065b9c547cSRui Paulo } 13075b9c547cSRui Paulo 13084bc52338SCy Schubert /* data delay protect */ 13094bc52338SCy Schubert body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME; 13104bc52338SCy Schubert /* lowest accept packet number */ 13115b9c547cSRui Paulo pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); 1312780fb4a2SCy Schubert if (pn > kay->pn_exhaustion) { 13135b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); 13145b9c547cSRui Paulo if (participant->is_key_server) 13155b9c547cSRui Paulo participant->new_sak = TRUE; 13165b9c547cSRui Paulo } 13175b9c547cSRui Paulo 13185b9c547cSRui Paulo body->llpn = host_to_be32(pn); 13195b9c547cSRui Paulo pn = ieee802_1x_mka_get_lpn(participant, &participant->oki); 13205b9c547cSRui Paulo body->olpn = host_to_be32(pn); 13215b9c547cSRui Paulo 13225b9c547cSRui Paulo /* plain tx, plain rx */ 1323780fb4a2SCy Schubert body->ptx = !kay->macsec_protect; 1324780fb4a2SCy Schubert body->prx = kay->macsec_validate != Strict; 13255b9c547cSRui Paulo 13265b9c547cSRui Paulo /* latest key: rx, tx, key server member identifier key number */ 13275b9c547cSRui Paulo body->lan = participant->lan; 1328780fb4a2SCy Schubert os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi)); 13295b9c547cSRui Paulo body->lkn = host_to_be32(participant->lki.kn); 13305b9c547cSRui Paulo body->lrx = participant->lrx; 13315b9c547cSRui Paulo body->ltx = participant->ltx; 13325b9c547cSRui Paulo 13335b9c547cSRui Paulo /* old key: rx, tx, key server member identifier key number */ 13345b9c547cSRui Paulo body->oan = participant->oan; 13355b9c547cSRui Paulo if (participant->oki.kn != participant->lki.kn && 13365b9c547cSRui Paulo participant->oki.kn != 0) { 13375b9c547cSRui Paulo body->otx = TRUE; 13385b9c547cSRui Paulo body->orx = TRUE; 13395b9c547cSRui Paulo os_memcpy(body->osrv_mi, participant->oki.mi, 13405b9c547cSRui Paulo sizeof(body->osrv_mi)); 13415b9c547cSRui Paulo body->okn = host_to_be32(participant->oki.kn); 13425b9c547cSRui Paulo } else { 13435b9c547cSRui Paulo body->otx = FALSE; 13445b9c547cSRui Paulo body->orx = FALSE; 13455b9c547cSRui Paulo } 13465b9c547cSRui Paulo 13475b9c547cSRui Paulo /* set CP's variable */ 13485b9c547cSRui Paulo if (body->ltx) { 1349780fb4a2SCy Schubert kay->tx_enable = TRUE; 1350780fb4a2SCy Schubert kay->port_enable = TRUE; 13515b9c547cSRui Paulo } 1352780fb4a2SCy Schubert if (body->lrx) 1353780fb4a2SCy Schubert kay->rx_enable = TRUE; 13545b9c547cSRui Paulo 13555b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body); 13565b9c547cSRui Paulo return 0; 13575b9c547cSRui Paulo } 13585b9c547cSRui Paulo 13595b9c547cSRui Paulo 13605b9c547cSRui Paulo /** 13615b9c547cSRui Paulo * ieee802_1x_mka_decode_sak_use_body - 13625b9c547cSRui Paulo */ 13635b9c547cSRui Paulo static int 13645b9c547cSRui Paulo ieee802_1x_mka_decode_sak_use_body( 13655b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 13665b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 13675b9c547cSRui Paulo { 13685b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 13695b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body; 13705b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 13714bc52338SCy Schubert struct receive_sc *rxsc; 13724bc52338SCy Schubert struct receive_sa *rxsa; 13735b9c547cSRui Paulo struct data_key *sa_key = NULL; 13745b9c547cSRui Paulo size_t body_len; 13755b9c547cSRui Paulo struct ieee802_1x_mka_ki ki; 13765b9c547cSRui Paulo u32 lpn; 13775b9c547cSRui Paulo Boolean all_receiving; 1378780fb4a2SCy Schubert Boolean found; 1379780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 13805b9c547cSRui Paulo 13815b9c547cSRui Paulo if (!participant->principal) { 13825b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); 13835b9c547cSRui Paulo return -1; 13845b9c547cSRui Paulo } 13855b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant, 13865b9c547cSRui Paulo participant->current_peer_id.mi); 13875b9c547cSRui Paulo if (!peer) { 13884bc52338SCy Schubert wpa_printf(MSG_WARNING, 13894bc52338SCy Schubert "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set", 13904bc52338SCy Schubert mi_txt(participant->current_peer_id.mi)); 13915b9c547cSRui Paulo return -1; 13925b9c547cSRui Paulo } 13935b9c547cSRui Paulo 13945b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 13955b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 13965b9c547cSRui Paulo body = (struct ieee802_1x_mka_sak_use_body *) mka_msg; 13975b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body); 13985b9c547cSRui Paulo 13995b9c547cSRui Paulo if ((body_len != 0) && (body_len < 40)) { 14005b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1401780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets", 1402780fb4a2SCy Schubert body_len); 14035b9c547cSRui Paulo return -1; 14045b9c547cSRui Paulo } 14055b9c547cSRui Paulo 14065b9c547cSRui Paulo /* TODO: what action should I take when peer does not support MACsec */ 14075b9c547cSRui Paulo if (body_len == 0) { 14085b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec"); 14095b9c547cSRui Paulo return 0; 14105b9c547cSRui Paulo } 14115b9c547cSRui Paulo 14125b9c547cSRui Paulo /* TODO: when the plain tx or rx of peer is true, should I change 14135b9c547cSRui Paulo * the attribute of controlled port 14145b9c547cSRui Paulo */ 14155b9c547cSRui Paulo if (body->prx) 14165b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE"); 14175b9c547cSRui Paulo 14185b9c547cSRui Paulo if (body->ptx) 14195b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE"); 14205b9c547cSRui Paulo 14215b9c547cSRui Paulo /* check latest key is valid */ 14225b9c547cSRui Paulo if (body->ltx || body->lrx) { 1423780fb4a2SCy Schubert found = FALSE; 14245b9c547cSRui Paulo os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); 1425780fb4a2SCy Schubert ki.kn = be_to_host32(body->lkn); 14265b9c547cSRui Paulo dl_list_for_each(sa_key, &participant->sak_list, 14275b9c547cSRui Paulo struct data_key, list) { 14285b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, &ki)) { 1429780fb4a2SCy Schubert found = TRUE; 14305b9c547cSRui Paulo break; 14315b9c547cSRui Paulo } 14325b9c547cSRui Paulo } 1433780fb4a2SCy Schubert if (!found) { 14344bc52338SCy Schubert wpa_printf(MSG_INFO, "KaY: Latest key is invalid"); 14355b9c547cSRui Paulo return -1; 14365b9c547cSRui Paulo } 14375b9c547cSRui Paulo if (os_memcmp(participant->lki.mi, body->lsrv_mi, 14385b9c547cSRui Paulo sizeof(participant->lki.mi)) == 0 && 1439780fb4a2SCy Schubert be_to_host32(body->lkn) == participant->lki.kn && 14405b9c547cSRui Paulo body->lan == participant->lan) { 14415b9c547cSRui Paulo peer->sak_used = TRUE; 14425b9c547cSRui Paulo } 14435b9c547cSRui Paulo if (body->ltx && peer->is_key_server) { 1444780fb4a2SCy Schubert ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); 1445780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 14465b9c547cSRui Paulo } 14475b9c547cSRui Paulo } 14485b9c547cSRui Paulo 144985732ac8SCy Schubert /* check old key is valid (but only if we remember our old key) */ 145085732ac8SCy Schubert if (participant->oki.kn != 0 && (body->otx || body->orx)) { 14515b9c547cSRui Paulo if (os_memcmp(participant->oki.mi, body->osrv_mi, 14525b9c547cSRui Paulo sizeof(participant->oki.mi)) != 0 || 1453780fb4a2SCy Schubert be_to_host32(body->okn) != participant->oki.kn || 14545b9c547cSRui Paulo body->oan != participant->oan) { 14555b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); 14565b9c547cSRui Paulo return -1; 14575b9c547cSRui Paulo } 14585b9c547cSRui Paulo } 14595b9c547cSRui Paulo 14605b9c547cSRui Paulo /* TODO: how to set the MACsec hardware when delay_protect is true */ 1461780fb4a2SCy Schubert if (body->delay_protect && 1462780fb4a2SCy Schubert (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { 14635b9c547cSRui Paulo wpa_printf(MSG_WARNING, 14644bc52338SCy Schubert "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE"); 14655b9c547cSRui Paulo return -1; 14665b9c547cSRui Paulo } 14675b9c547cSRui Paulo 14685b9c547cSRui Paulo /* check all live peer have used the sak for receiving sa */ 14695b9c547cSRui Paulo all_receiving = TRUE; 14705b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 14715b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 14725b9c547cSRui Paulo if (!peer->sak_used) { 14735b9c547cSRui Paulo all_receiving = FALSE; 14745b9c547cSRui Paulo break; 14755b9c547cSRui Paulo } 14765b9c547cSRui Paulo } 14775b9c547cSRui Paulo if (all_receiving) { 14785b9c547cSRui Paulo participant->to_dist_sak = FALSE; 1479780fb4a2SCy Schubert ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); 1480780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 14815b9c547cSRui Paulo } 14825b9c547cSRui Paulo 14834bc52338SCy Schubert /* if I'm key server, and detects peer member pn exhaustion, rekey. */ 1484780fb4a2SCy Schubert lpn = be_to_host32(body->llpn); 1485780fb4a2SCy Schubert if (lpn > kay->pn_exhaustion) { 14865b9c547cSRui Paulo if (participant->is_key_server) { 14875b9c547cSRui Paulo participant->new_sak = TRUE; 14885b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); 14895b9c547cSRui Paulo } 14905b9c547cSRui Paulo } 14915b9c547cSRui Paulo 14924bc52338SCy Schubert if (sa_key) 14934bc52338SCy Schubert sa_key->next_pn = lpn; 1494780fb4a2SCy Schubert found = FALSE; 14954bc52338SCy Schubert dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc, 14964bc52338SCy Schubert list) { 14974bc52338SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, 14984bc52338SCy Schubert list) { 14994bc52338SCy Schubert if (sa_key && rxsa->pkey == sa_key) { 1500780fb4a2SCy Schubert found = TRUE; 15015b9c547cSRui Paulo break; 15025b9c547cSRui Paulo } 15035b9c547cSRui Paulo } 15044bc52338SCy Schubert if (found) 15054bc52338SCy Schubert break; 15064bc52338SCy Schubert } 1507780fb4a2SCy Schubert if (!found) { 15084bc52338SCy Schubert wpa_printf(MSG_WARNING, "KaY: Can't find rxsa"); 15095b9c547cSRui Paulo return -1; 15105b9c547cSRui Paulo } 15115b9c547cSRui Paulo 15124bc52338SCy Schubert if (body->delay_protect) { 15134bc52338SCy Schubert secy_get_receive_lowest_pn(participant->kay, rxsa); 15144bc52338SCy Schubert if (lpn > rxsa->lowest_pn) { 15154bc52338SCy Schubert /* Delay protect window (communicated via MKA) is 15164bc52338SCy Schubert * tighter than SecY's current replay protect window, 15174bc52338SCy Schubert * so tell SecY the new (and higher) lpn. */ 15184bc52338SCy Schubert rxsa->lowest_pn = lpn; 15194bc52338SCy Schubert secy_set_receive_lowest_pn(participant->kay, rxsa); 15204bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn); 15214bc52338SCy Schubert } 15224bc52338SCy Schubert /* FIX: Delay protection for olpn not implemented. 15234bc52338SCy Schubert * Note that Old Key is only active for MKA_SAK_RETIRE_TIME 15244bc52338SCy Schubert * (3 seconds) and delay protection does allow PN's within 15254bc52338SCy Schubert * a 2 seconds window, so olpn would be a lot of work for 15264bc52338SCy Schubert * just 1 second's worth of protection. */ 15275b9c547cSRui Paulo } 15285b9c547cSRui Paulo 15295b9c547cSRui Paulo return 0; 15305b9c547cSRui Paulo } 15315b9c547cSRui Paulo 15325b9c547cSRui Paulo 15335b9c547cSRui Paulo /** 15345b9c547cSRui Paulo * ieee802_1x_mka_dist_sak_body_present 15355b9c547cSRui Paulo */ 15365b9c547cSRui Paulo static Boolean 15375b9c547cSRui Paulo ieee802_1x_mka_dist_sak_body_present( 15385b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 15395b9c547cSRui Paulo { 15404bc52338SCy Schubert return participant->is_key_server && participant->to_dist_sak && 15414bc52338SCy Schubert participant->new_key; 15425b9c547cSRui Paulo } 15435b9c547cSRui Paulo 15445b9c547cSRui Paulo 15455b9c547cSRui Paulo /** 15465b9c547cSRui Paulo * ieee802_1x_kay_get_dist_sak_length 15475b9c547cSRui Paulo */ 15485b9c547cSRui Paulo static int 15495b9c547cSRui Paulo ieee802_1x_mka_get_dist_sak_length( 15505b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 15515b9c547cSRui Paulo { 1552780fb4a2SCy Schubert int length = MKA_HDR_LEN; 1553780fb4a2SCy Schubert unsigned int cs_index = participant->kay->macsec_csindex; 15545b9c547cSRui Paulo 1555780fb4a2SCy Schubert if (participant->advised_desired && cs_index < CS_TABLE_SIZE) { 15565b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_dist_sak_body); 15575b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX) 15585b9c547cSRui Paulo length += CS_ID_LEN; 15595b9c547cSRui Paulo 15605b9c547cSRui Paulo length += cipher_suite_tbl[cs_index].sak_len + 8; 15615b9c547cSRui Paulo } 15625b9c547cSRui Paulo 1563780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 15645b9c547cSRui Paulo } 15655b9c547cSRui Paulo 15665b9c547cSRui Paulo 15675b9c547cSRui Paulo /** 15685b9c547cSRui Paulo * ieee802_1x_mka_encode_dist_sak_body - 15695b9c547cSRui Paulo */ 15705b9c547cSRui Paulo static int 15715b9c547cSRui Paulo ieee802_1x_mka_encode_dist_sak_body( 15725b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 15735b9c547cSRui Paulo struct wpabuf *buf) 15745b9c547cSRui Paulo { 15755b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body; 15765b9c547cSRui Paulo struct data_key *sak; 15775b9c547cSRui Paulo unsigned int length; 1578780fb4a2SCy Schubert unsigned int cs_index; 15795b9c547cSRui Paulo int sak_pos; 15805b9c547cSRui Paulo 15815b9c547cSRui Paulo length = ieee802_1x_mka_get_dist_sak_length(participant); 15825b9c547cSRui Paulo body = wpabuf_put(buf, length); 15835b9c547cSRui Paulo body->type = MKA_DISTRIBUTED_SAK; 15845b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN); 15855b9c547cSRui Paulo if (length == MKA_HDR_LEN) { 15865b9c547cSRui Paulo body->confid_offset = 0; 15875b9c547cSRui Paulo body->dan = 0; 15885b9c547cSRui Paulo return 0; 15895b9c547cSRui Paulo } 15905b9c547cSRui Paulo 15915b9c547cSRui Paulo sak = participant->new_key; 15924bc52338SCy Schubert if (!sak) { 15934bc52338SCy Schubert wpa_printf(MSG_DEBUG, 15944bc52338SCy Schubert "KaY: No SAK available to build Distributed SAK parameter set"); 15954bc52338SCy Schubert return -1; 15964bc52338SCy Schubert } 15975b9c547cSRui Paulo body->confid_offset = sak->confidentiality_offset; 15985b9c547cSRui Paulo body->dan = sak->an; 15995b9c547cSRui Paulo body->kn = host_to_be32(sak->key_identifier.kn); 16005b9c547cSRui Paulo cs_index = participant->kay->macsec_csindex; 16015b9c547cSRui Paulo sak_pos = 0; 1602780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE) 1603780fb4a2SCy Schubert return -1; 16045b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX) { 1605780fb4a2SCy Schubert be64 cs; 1606780fb4a2SCy Schubert 1607780fb4a2SCy Schubert cs = host_to_be64(cipher_suite_tbl[cs_index].id); 1608780fb4a2SCy Schubert os_memcpy(body->sak, &cs, CS_ID_LEN); 16095b9c547cSRui Paulo sak_pos = CS_ID_LEN; 16105b9c547cSRui Paulo } 16114bc52338SCy Schubert if (aes_wrap(participant->kek.key, participant->kek.len, 16125b9c547cSRui Paulo cipher_suite_tbl[cs_index].sak_len / 8, 16135b9c547cSRui Paulo sak->key, body->sak + sak_pos)) { 16145b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); 16155b9c547cSRui Paulo return -1; 16165b9c547cSRui Paulo } 16175b9c547cSRui Paulo 16185b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body); 16195b9c547cSRui Paulo 16205b9c547cSRui Paulo return 0; 16215b9c547cSRui Paulo } 16225b9c547cSRui Paulo 16235b9c547cSRui Paulo 16245b9c547cSRui Paulo /** 16255b9c547cSRui Paulo * ieee802_1x_kay_init_data_key - 16265b9c547cSRui Paulo */ 1627780fb4a2SCy Schubert static void ieee802_1x_kay_init_data_key(struct data_key *pkey) 16285b9c547cSRui Paulo { 1629780fb4a2SCy Schubert pkey->transmits = TRUE; 1630780fb4a2SCy Schubert pkey->receives = TRUE; 16315b9c547cSRui Paulo os_get_time(&pkey->created_time); 16325b9c547cSRui Paulo 16334bc52338SCy Schubert pkey->next_pn = 1; 16345b9c547cSRui Paulo pkey->user = 1; 16355b9c547cSRui Paulo } 16365b9c547cSRui Paulo 16375b9c547cSRui Paulo 16385b9c547cSRui Paulo /** 16395b9c547cSRui Paulo * ieee802_1x_kay_decode_dist_sak_body - 16405b9c547cSRui Paulo */ 16415b9c547cSRui Paulo static int 16425b9c547cSRui Paulo ieee802_1x_mka_decode_dist_sak_body( 16435b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 16445b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 16455b9c547cSRui Paulo { 16465b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 16475b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body; 16485b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 16495b9c547cSRui Paulo struct macsec_ciphersuite *cs; 16505b9c547cSRui Paulo size_t body_len; 16515b9c547cSRui Paulo struct data_key *sa_key = NULL; 16525b9c547cSRui Paulo int sak_len; 16535b9c547cSRui Paulo u8 *wrap_sak; 16545b9c547cSRui Paulo u8 *unwrap_sak; 1655780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay; 16565b9c547cSRui Paulo 16575b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 16585b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 16595b9c547cSRui Paulo if ((body_len != 0) && (body_len != 28) && (body_len < 36)) { 16605b9c547cSRui Paulo wpa_printf(MSG_ERROR, 1661780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets", 1662780fb4a2SCy Schubert body_len); 16635b9c547cSRui Paulo return -1; 16645b9c547cSRui Paulo } 16655b9c547cSRui Paulo 16665b9c547cSRui Paulo if (!participant->principal) { 16675b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16685b9c547cSRui Paulo "KaY: I can't accept the distributed SAK as I am not principal"); 16695b9c547cSRui Paulo return -1; 16705b9c547cSRui Paulo } 16715b9c547cSRui Paulo if (participant->is_key_server) { 16725b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16734bc52338SCy Schubert "KaY: Reject distributed SAK since I'm a key server"); 16745b9c547cSRui Paulo return -1; 16755b9c547cSRui Paulo } 1676780fb4a2SCy Schubert if (!kay->macsec_desired || 1677780fb4a2SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 16785b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16795b9c547cSRui Paulo "KaY: I am not MACsec-desired or without MACsec capable"); 16805b9c547cSRui Paulo return -1; 16815b9c547cSRui Paulo } 16825b9c547cSRui Paulo 16835b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant, 16845b9c547cSRui Paulo participant->current_peer_id.mi); 16855b9c547cSRui Paulo if (!peer) { 16865b9c547cSRui Paulo wpa_printf(MSG_ERROR, 16875b9c547cSRui Paulo "KaY: The key server is not in my live peers list"); 16885b9c547cSRui Paulo return -1; 16895b9c547cSRui Paulo } 1690780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &peer->sci)) { 16915b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: The key server is not elected"); 16925b9c547cSRui Paulo return -1; 16935b9c547cSRui Paulo } 1694780fb4a2SCy Schubert 16955b9c547cSRui Paulo if (body_len == 0) { 1696780fb4a2SCy Schubert kay->authenticated = TRUE; 1697780fb4a2SCy Schubert kay->secured = FALSE; 1698780fb4a2SCy Schubert kay->failed = FALSE; 16995b9c547cSRui Paulo participant->advised_desired = FALSE; 1700780fb4a2SCy Schubert ieee802_1x_cp_connect_authenticated(kay->cp); 1701780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 17025b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec"); 170385732ac8SCy Schubert participant->to_use_sak = FALSE; 17045b9c547cSRui Paulo return 0; 17055b9c547cSRui Paulo } 1706780fb4a2SCy Schubert 17075b9c547cSRui Paulo participant->advised_desired = TRUE; 1708780fb4a2SCy Schubert kay->authenticated = FALSE; 1709780fb4a2SCy Schubert kay->secured = TRUE; 1710780fb4a2SCy Schubert kay->failed = FALSE; 1711780fb4a2SCy Schubert ieee802_1x_cp_connect_secure(kay->cp); 1712780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 17135b9c547cSRui Paulo 17145b9c547cSRui Paulo body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg; 17155b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body); 17165b9c547cSRui Paulo dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list) 17175b9c547cSRui Paulo { 17185b9c547cSRui Paulo if (os_memcmp(sa_key->key_identifier.mi, 17195b9c547cSRui Paulo participant->current_peer_id.mi, MI_LEN) == 0 && 17205b9c547cSRui Paulo sa_key->key_identifier.kn == be_to_host32(body->kn)) { 17214bc52338SCy Schubert wpa_printf(MSG_DEBUG, 17224bc52338SCy Schubert "KaY: SAK has already been installed - do not set it again"); 17235b9c547cSRui Paulo return 0; 17245b9c547cSRui Paulo } 17255b9c547cSRui Paulo } 1726780fb4a2SCy Schubert 17275b9c547cSRui Paulo if (body_len == 28) { 17285b9c547cSRui Paulo sak_len = DEFAULT_SA_KEY_LEN; 17295b9c547cSRui Paulo wrap_sak = body->sak; 1730780fb4a2SCy Schubert kay->macsec_csindex = DEFAULT_CS_INDEX; 1731780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex]; 17325b9c547cSRui Paulo } else { 17334bc52338SCy Schubert unsigned int idx; 17344bc52338SCy Schubert 17354bc52338SCy Schubert cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak, 17364bc52338SCy Schubert &idx); 17375b9c547cSRui Paulo if (!cs) { 17385b9c547cSRui Paulo wpa_printf(MSG_ERROR, 17395b9c547cSRui Paulo "KaY: I can't support the Cipher Suite advised by key server"); 17405b9c547cSRui Paulo return -1; 17415b9c547cSRui Paulo } 17425b9c547cSRui Paulo sak_len = cs->sak_len; 17435b9c547cSRui Paulo wrap_sak = body->sak + CS_ID_LEN; 17444bc52338SCy Schubert kay->macsec_csindex = idx; 17455b9c547cSRui Paulo } 17465b9c547cSRui Paulo 17475b9c547cSRui Paulo unwrap_sak = os_zalloc(sak_len); 17485b9c547cSRui Paulo if (!unwrap_sak) { 17495b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 17505b9c547cSRui Paulo return -1; 17515b9c547cSRui Paulo } 17524bc52338SCy Schubert if (aes_unwrap(participant->kek.key, participant->kek.len, 17534bc52338SCy Schubert sak_len >> 3, wrap_sak, unwrap_sak)) { 17545b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); 17555b9c547cSRui Paulo os_free(unwrap_sak); 17565b9c547cSRui Paulo return -1; 17575b9c547cSRui Paulo } 17584bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:", 175985732ac8SCy Schubert unwrap_sak, sak_len); 17605b9c547cSRui Paulo 1761780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key)); 17625b9c547cSRui Paulo if (!sa_key) { 17635b9c547cSRui Paulo os_free(unwrap_sak); 17645b9c547cSRui Paulo return -1; 17655b9c547cSRui Paulo } 17665b9c547cSRui Paulo 1767780fb4a2SCy Schubert os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi, 1768780fb4a2SCy Schubert MI_LEN); 1769780fb4a2SCy Schubert sa_key->key_identifier.kn = be_to_host32(body->kn); 1770780fb4a2SCy Schubert 1771780fb4a2SCy Schubert sa_key->key = unwrap_sak; 1772780fb4a2SCy Schubert sa_key->key_len = sak_len; 1773780fb4a2SCy Schubert 1774780fb4a2SCy Schubert sa_key->confidentiality_offset = body->confid_offset; 1775780fb4a2SCy Schubert sa_key->an = body->dan; 1776780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key); 1777780fb4a2SCy Schubert 177885732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key); 17795b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list); 17805b9c547cSRui Paulo 1781780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); 1782780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 1783780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, body->confid_offset); 1784780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 1785780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); 1786780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, body->dan); 1787780fb4a2SCy Schubert ieee802_1x_cp_signal_newsak(kay->cp); 1788780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp); 17895b9c547cSRui Paulo 179085732ac8SCy Schubert kay->rcvd_keys++; 17915b9c547cSRui Paulo participant->to_use_sak = TRUE; 17925b9c547cSRui Paulo 17935b9c547cSRui Paulo return 0; 17945b9c547cSRui Paulo } 17955b9c547cSRui Paulo 17965b9c547cSRui Paulo 17975b9c547cSRui Paulo /** 17985b9c547cSRui Paulo * ieee802_1x_mka_icv_body_present 17995b9c547cSRui Paulo */ 18005b9c547cSRui Paulo static Boolean 18015b9c547cSRui Paulo ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant) 18025b9c547cSRui Paulo { 18035b9c547cSRui Paulo return TRUE; 18045b9c547cSRui Paulo } 18055b9c547cSRui Paulo 18065b9c547cSRui Paulo 18075b9c547cSRui Paulo /** 18085b9c547cSRui Paulo * ieee802_1x_kay_get_icv_length 18095b9c547cSRui Paulo */ 18105b9c547cSRui Paulo static int 18115b9c547cSRui Paulo ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) 18125b9c547cSRui Paulo { 18135b9c547cSRui Paulo int length; 18145b9c547cSRui Paulo 18154bc52338SCy Schubert /* Determine if we need space for the ICV Indicator */ 18164bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != 18174bc52338SCy Schubert DEFAULT_ICV_LEN) 18185b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_icv_body); 18194bc52338SCy Schubert else 18204bc52338SCy Schubert length = 0; 18215b9c547cSRui Paulo length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; 18225b9c547cSRui Paulo 1823780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length); 18245b9c547cSRui Paulo } 18255b9c547cSRui Paulo 18265b9c547cSRui Paulo 18275b9c547cSRui Paulo /** 18285b9c547cSRui Paulo * ieee802_1x_mka_encode_icv_body - 18295b9c547cSRui Paulo */ 18305b9c547cSRui Paulo static int 18315b9c547cSRui Paulo ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, 18325b9c547cSRui Paulo struct wpabuf *buf) 18335b9c547cSRui Paulo { 18345b9c547cSRui Paulo struct ieee802_1x_mka_icv_body *body; 18355b9c547cSRui Paulo unsigned int length; 18365b9c547cSRui Paulo u8 cmac[MAX_ICV_LEN]; 18375b9c547cSRui Paulo 18385b9c547cSRui Paulo length = ieee802_1x_mka_get_icv_length(participant); 18394bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != 18404bc52338SCy Schubert DEFAULT_ICV_LEN) { 18414bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: ICV Indicator"); 18425b9c547cSRui Paulo body = wpabuf_put(buf, MKA_HDR_LEN); 18435b9c547cSRui Paulo body->type = MKA_ICV_INDICATOR; 18444bc52338SCy Schubert length -= MKA_HDR_LEN; 18454bc52338SCy Schubert set_mka_param_body_len(body, length); 18465b9c547cSRui Paulo } 18475b9c547cSRui Paulo 18485b9c547cSRui Paulo if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash( 18494bc52338SCy Schubert participant->ick.key, participant->ick.len, 18504bc52338SCy Schubert wpabuf_head(buf), wpabuf_len(buf), cmac)) { 18514bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV"); 18525b9c547cSRui Paulo return -1; 18535b9c547cSRui Paulo } 18544bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length); 18555b9c547cSRui Paulo 18565b9c547cSRui Paulo os_memcpy(wpabuf_put(buf, length), cmac, length); 18575b9c547cSRui Paulo 18585b9c547cSRui Paulo return 0; 18595b9c547cSRui Paulo } 18605b9c547cSRui Paulo 18615b9c547cSRui Paulo /** 18625b9c547cSRui Paulo * ieee802_1x_mka_decode_icv_body - 18635b9c547cSRui Paulo */ 18644bc52338SCy Schubert static const u8 * 18655b9c547cSRui Paulo ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, 18665b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 18675b9c547cSRui Paulo { 18684bc52338SCy Schubert const struct ieee802_1x_mka_hdr *hdr; 18694bc52338SCy Schubert const struct ieee802_1x_mka_icv_body *body; 18705b9c547cSRui Paulo size_t body_len; 18715b9c547cSRui Paulo size_t left_len; 1872780fb4a2SCy Schubert u8 body_type; 18735b9c547cSRui Paulo const u8 *pos; 18745b9c547cSRui Paulo 18755b9c547cSRui Paulo pos = mka_msg; 18765b9c547cSRui Paulo left_len = msg_len; 18774bc52338SCy Schubert while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { 18784bc52338SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) pos; 18794bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 18805b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr); 18815b9c547cSRui Paulo 18824bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN) 18835b9c547cSRui Paulo break; 18845b9c547cSRui Paulo 18855b9c547cSRui Paulo if (body_type != MKA_ICV_INDICATOR) { 18865b9c547cSRui Paulo left_len -= MKA_HDR_LEN + body_len; 18875b9c547cSRui Paulo pos += MKA_HDR_LEN + body_len; 18885b9c547cSRui Paulo continue; 18895b9c547cSRui Paulo } 18905b9c547cSRui Paulo 18914bc52338SCy Schubert body = (const struct ieee802_1x_mka_icv_body *) pos; 18925b9c547cSRui Paulo if (body_len 18934bc52338SCy Schubert < mka_alg_tbl[participant->kay->mka_algindex].icv_len) 18945b9c547cSRui Paulo return NULL; 18955b9c547cSRui Paulo 18965b9c547cSRui Paulo return body->icv; 18975b9c547cSRui Paulo } 18985b9c547cSRui Paulo 18994bc52338SCy Schubert return mka_msg + msg_len - DEFAULT_ICV_LEN; 19005b9c547cSRui Paulo } 19015b9c547cSRui Paulo 19025b9c547cSRui Paulo 19035b9c547cSRui Paulo /** 19045b9c547cSRui Paulo * ieee802_1x_mka_decode_dist_cak_body- 19055b9c547cSRui Paulo */ 19065b9c547cSRui Paulo static int 19075b9c547cSRui Paulo ieee802_1x_mka_decode_dist_cak_body( 19085b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 19095b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19105b9c547cSRui Paulo { 19115b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 19125b9c547cSRui Paulo size_t body_len; 19135b9c547cSRui Paulo 19145b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 19155b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 19165b9c547cSRui Paulo if (body_len < 28) { 19175b9c547cSRui Paulo wpa_printf(MSG_ERROR, 19184bc52338SCy Schubert "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets", 1919780fb4a2SCy Schubert body_len); 19205b9c547cSRui Paulo return -1; 19215b9c547cSRui Paulo } 19225b9c547cSRui Paulo 19235b9c547cSRui Paulo return 0; 19245b9c547cSRui Paulo } 19255b9c547cSRui Paulo 19265b9c547cSRui Paulo 19275b9c547cSRui Paulo /** 19285b9c547cSRui Paulo * ieee802_1x_mka_decode_kmd_body - 19295b9c547cSRui Paulo */ 19305b9c547cSRui Paulo static int 19315b9c547cSRui Paulo ieee802_1x_mka_decode_kmd_body( 19325b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 19335b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19345b9c547cSRui Paulo { 19355b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 19365b9c547cSRui Paulo size_t body_len; 19375b9c547cSRui Paulo 19385b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg; 19395b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr); 19405b9c547cSRui Paulo if (body_len < 5) { 19415b9c547cSRui Paulo wpa_printf(MSG_ERROR, 19424bc52338SCy Schubert "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets", 1943780fb4a2SCy Schubert body_len); 19445b9c547cSRui Paulo return -1; 19455b9c547cSRui Paulo } 19465b9c547cSRui Paulo 19475b9c547cSRui Paulo return 0; 19485b9c547cSRui Paulo } 19495b9c547cSRui Paulo 19505b9c547cSRui Paulo 19515b9c547cSRui Paulo /** 19525b9c547cSRui Paulo * ieee802_1x_mka_decode_announce_body - 19535b9c547cSRui Paulo */ 19545b9c547cSRui Paulo static int ieee802_1x_mka_decode_announce_body( 19555b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, 19565b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len) 19575b9c547cSRui Paulo { 19585b9c547cSRui Paulo return 0; 19595b9c547cSRui Paulo } 19605b9c547cSRui Paulo 19615b9c547cSRui Paulo 1962780fb4a2SCy Schubert struct mka_param_body_handler { 1963780fb4a2SCy Schubert int (*body_tx)(struct ieee802_1x_mka_participant *participant, 1964780fb4a2SCy Schubert struct wpabuf *buf); 1965780fb4a2SCy Schubert int (*body_rx)(struct ieee802_1x_mka_participant *participant, 1966780fb4a2SCy Schubert const u8 *mka_msg, size_t msg_len); 1967780fb4a2SCy Schubert int (*body_length)(struct ieee802_1x_mka_participant *participant); 1968780fb4a2SCy Schubert Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); 1969780fb4a2SCy Schubert }; 1970780fb4a2SCy Schubert 1971780fb4a2SCy Schubert 1972780fb4a2SCy Schubert static struct mka_param_body_handler mka_body_handler[] = { 19734bc52338SCy Schubert /* Basic parameter set */ 19745b9c547cSRui Paulo { 1975780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_basic_body, 1976780fb4a2SCy Schubert .body_rx = NULL, 1977780fb4a2SCy Schubert .body_length = ieee802_1x_mka_basic_body_length, 1978780fb4a2SCy Schubert .body_present = ieee802_1x_mka_basic_body_present 19795b9c547cSRui Paulo }, 19805b9c547cSRui Paulo 19814bc52338SCy Schubert /* Live Peer List parameter set */ 19825b9c547cSRui Paulo { 1983780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_live_peer_body, 1984780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_live_peer_body, 1985780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_live_peer_length, 1986780fb4a2SCy Schubert .body_present = ieee802_1x_mka_live_peer_body_present 19875b9c547cSRui Paulo }, 19885b9c547cSRui Paulo 19894bc52338SCy Schubert /* Potential Peer List parameter set */ 19905b9c547cSRui Paulo { 1991780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_potential_peer_body, 1992780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_potential_peer_body, 1993780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_potential_peer_length, 1994780fb4a2SCy Schubert .body_present = ieee802_1x_mka_potential_peer_body_present 19955b9c547cSRui Paulo }, 19965b9c547cSRui Paulo 19974bc52338SCy Schubert /* MACsec SAK Use parameter set */ 19985b9c547cSRui Paulo { 1999780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_sak_use_body, 2000780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_sak_use_body, 2001780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_sak_use_length, 2002780fb4a2SCy Schubert .body_present = ieee802_1x_mka_sak_use_body_present 20035b9c547cSRui Paulo }, 20045b9c547cSRui Paulo 20054bc52338SCy Schubert /* Distributed SAK parameter set */ 20065b9c547cSRui Paulo { 2007780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_dist_sak_body, 2008780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_sak_body, 2009780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_dist_sak_length, 2010780fb4a2SCy Schubert .body_present = ieee802_1x_mka_dist_sak_body_present 20115b9c547cSRui Paulo }, 20125b9c547cSRui Paulo 20134bc52338SCy Schubert /* Distribute CAK parameter set */ 20145b9c547cSRui Paulo { 2015780fb4a2SCy Schubert .body_tx = NULL, 2016780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_cak_body, 2017780fb4a2SCy Schubert .body_length = NULL, 2018780fb4a2SCy Schubert .body_present = NULL 20195b9c547cSRui Paulo }, 20205b9c547cSRui Paulo 20214bc52338SCy Schubert /* KMD parameter set */ 20225b9c547cSRui Paulo { 2023780fb4a2SCy Schubert .body_tx = NULL, 2024780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_kmd_body, 2025780fb4a2SCy Schubert .body_length = NULL, 2026780fb4a2SCy Schubert .body_present = NULL 20275b9c547cSRui Paulo }, 20285b9c547cSRui Paulo 20294bc52338SCy Schubert /* Announcement parameter set */ 20305b9c547cSRui Paulo { 2031780fb4a2SCy Schubert .body_tx = NULL, 2032780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_announce_body, 2033780fb4a2SCy Schubert .body_length = NULL, 2034780fb4a2SCy Schubert .body_present = NULL 20355b9c547cSRui Paulo }, 20365b9c547cSRui Paulo 20374bc52338SCy Schubert /* ICV Indicator parameter set */ 20385b9c547cSRui Paulo { 2039780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_icv_body, 2040780fb4a2SCy Schubert .body_rx = NULL, 2041780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_icv_length, 2042780fb4a2SCy Schubert .body_present = ieee802_1x_mka_icv_body_present 20435b9c547cSRui Paulo }, 20445b9c547cSRui Paulo }; 20455b9c547cSRui Paulo 20465b9c547cSRui Paulo 20475b9c547cSRui Paulo /** 204885732ac8SCy Schubert * ieee802_1x_kay_use_data_key - Take reference on a key 204985732ac8SCy Schubert */ 205085732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey) 205185732ac8SCy Schubert { 205285732ac8SCy Schubert pkey->user++; 205385732ac8SCy Schubert } 205485732ac8SCy Schubert 205585732ac8SCy Schubert 205685732ac8SCy Schubert /** 205785732ac8SCy Schubert * ieee802_1x_kay_deinit_data_key - Release reference on a key and 205885732ac8SCy Schubert * free if there are no remaining users 20595b9c547cSRui Paulo */ 2060780fb4a2SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) 20615b9c547cSRui Paulo { 20625b9c547cSRui Paulo if (!pkey) 20635b9c547cSRui Paulo return; 20645b9c547cSRui Paulo 20655b9c547cSRui Paulo pkey->user--; 20665b9c547cSRui Paulo if (pkey->user > 1) 20675b9c547cSRui Paulo return; 20685b9c547cSRui Paulo 20695b9c547cSRui Paulo os_free(pkey->key); 20705b9c547cSRui Paulo os_free(pkey); 20715b9c547cSRui Paulo } 20725b9c547cSRui Paulo 20735b9c547cSRui Paulo 20745b9c547cSRui Paulo /** 20755b9c547cSRui Paulo * ieee802_1x_kay_generate_new_sak - 20765b9c547cSRui Paulo */ 20775b9c547cSRui Paulo static int 20785b9c547cSRui Paulo ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) 20795b9c547cSRui Paulo { 20805b9c547cSRui Paulo struct data_key *sa_key = NULL; 20815b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 20825b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 20835b9c547cSRui Paulo int ctx_len, ctx_offset; 20845b9c547cSRui Paulo u8 *context; 2085780fb4a2SCy Schubert unsigned int key_len; 2086780fb4a2SCy Schubert u8 *key; 2087780fb4a2SCy Schubert struct macsec_ciphersuite *cs; 20885b9c547cSRui Paulo 20895b9c547cSRui Paulo /* check condition for generating a fresh SAK: 20905b9c547cSRui Paulo * must have one live peer 20915b9c547cSRui Paulo * and MKA life time elapse since last distribution 20925b9c547cSRui Paulo * or potential peer is empty 20935b9c547cSRui Paulo */ 20945b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 20955b9c547cSRui Paulo wpa_printf(MSG_ERROR, 20964bc52338SCy Schubert "KaY: Live peers list must not be empty when generating fresh SAK"); 20975b9c547cSRui Paulo return -1; 20985b9c547cSRui Paulo } 20995b9c547cSRui Paulo 21005b9c547cSRui Paulo /* FIXME: A fresh SAK not generated until 21015b9c547cSRui Paulo * the live peer list contains at least one peer and 21025b9c547cSRui Paulo * MKA life time has elapsed since the prior SAK was first distributed, 21035b9c547cSRui Paulo * or the Key server's potential peer is empty 21045b9c547cSRui Paulo * but I can't understand the second item, so 21055b9c547cSRui Paulo * here only check first item and ingore 21065b9c547cSRui Paulo * && (!dl_list_empty(&participant->potential_peers))) { 21075b9c547cSRui Paulo */ 21085b9c547cSRui Paulo if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) { 21095b9c547cSRui Paulo wpa_printf(MSG_ERROR, 21104bc52338SCy Schubert "KaY: Life time has not elapsed since prior SAK distributed"); 21115b9c547cSRui Paulo return -1; 21125b9c547cSRui Paulo } 21135b9c547cSRui Paulo 2114780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex]; 2115780fb4a2SCy Schubert key_len = cs->sak_len; 2116780fb4a2SCy Schubert key = os_zalloc(key_len); 2117780fb4a2SCy Schubert if (!key) { 21185b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 21195b9c547cSRui Paulo return -1; 21205b9c547cSRui Paulo } 21215b9c547cSRui Paulo 2122780fb4a2SCy Schubert ctx_len = key_len + sizeof(kay->dist_kn); 21235b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21245b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 21255b9c547cSRui Paulo ctx_len += sizeof(peer->mi); 21265b9c547cSRui Paulo ctx_len += sizeof(participant->mi); 21275b9c547cSRui Paulo 21285b9c547cSRui Paulo context = os_zalloc(ctx_len); 2129780fb4a2SCy Schubert if (!context) 2130780fb4a2SCy Schubert goto fail; 2131780fb4a2SCy Schubert 21325b9c547cSRui Paulo ctx_offset = 0; 2133780fb4a2SCy Schubert if (os_get_random(context + ctx_offset, key_len) < 0) 2134780fb4a2SCy Schubert goto fail; 2135780fb4a2SCy Schubert 2136780fb4a2SCy Schubert ctx_offset += key_len; 21375b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21385b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 21395b9c547cSRui Paulo os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi)); 21405b9c547cSRui Paulo ctx_offset += sizeof(peer->mi); 21415b9c547cSRui Paulo } 21425b9c547cSRui Paulo os_memcpy(context + ctx_offset, participant->mi, 21435b9c547cSRui Paulo sizeof(participant->mi)); 21445b9c547cSRui Paulo ctx_offset += sizeof(participant->mi); 21455b9c547cSRui Paulo os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); 21465b9c547cSRui Paulo 21474bc52338SCy Schubert if (key_len == 16 || key_len == 32) { 21484bc52338SCy Schubert if (ieee802_1x_sak_aes_cmac(participant->cak.key, 21494bc52338SCy Schubert participant->cak.len, 21504bc52338SCy Schubert context, ctx_len, 21514bc52338SCy Schubert key, key_len)) { 21524bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK"); 21534bc52338SCy Schubert goto fail; 21544bc52338SCy Schubert } 21555b9c547cSRui Paulo } else { 21564bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported", 21574bc52338SCy Schubert key_len); 2158780fb4a2SCy Schubert goto fail; 21595b9c547cSRui Paulo } 216085732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); 2161780fb4a2SCy Schubert os_free(context); 2162780fb4a2SCy Schubert context = NULL; 21635b9c547cSRui Paulo 2164780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key)); 21655b9c547cSRui Paulo if (!sa_key) { 2166780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); 2167780fb4a2SCy Schubert goto fail; 21685b9c547cSRui Paulo } 2169780fb4a2SCy Schubert 2170780fb4a2SCy Schubert sa_key->key = key; 2171780fb4a2SCy Schubert sa_key->key_len = key_len; 2172780fb4a2SCy Schubert os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN); 2173780fb4a2SCy Schubert sa_key->key_identifier.kn = kay->dist_kn; 2174780fb4a2SCy Schubert 2175780fb4a2SCy Schubert sa_key->confidentiality_offset = kay->macsec_confidentiality; 2176780fb4a2SCy Schubert sa_key->an = kay->dist_an; 2177780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key); 2178780fb4a2SCy Schubert 21795b9c547cSRui Paulo participant->new_key = sa_key; 21805b9c547cSRui Paulo 218185732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key); 21825b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list); 218385732ac8SCy Schubert 2184780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); 21855b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 2186780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); 21875b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 2188780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); 2189780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an); 21905b9c547cSRui Paulo ieee802_1x_cp_signal_newsak(kay->cp); 21915b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 21925b9c547cSRui Paulo 21935b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 21945b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) 21955b9c547cSRui Paulo peer->sak_used = FALSE; 21965b9c547cSRui Paulo 2197780fb4a2SCy Schubert kay->dist_kn++; 2198780fb4a2SCy Schubert kay->dist_an++; 2199780fb4a2SCy Schubert if (kay->dist_an > 3) 2200780fb4a2SCy Schubert kay->dist_an = 0; 22015b9c547cSRui Paulo 2202780fb4a2SCy Schubert kay->dist_time = time(NULL); 22035b9c547cSRui Paulo 22045b9c547cSRui Paulo return 0; 2205780fb4a2SCy Schubert 2206780fb4a2SCy Schubert fail: 2207780fb4a2SCy Schubert os_free(key); 2208780fb4a2SCy Schubert os_free(context); 2209780fb4a2SCy Schubert return -1; 2210780fb4a2SCy Schubert } 2211780fb4a2SCy Schubert 2212780fb4a2SCy Schubert 2213780fb4a2SCy Schubert static int compare_priorities(const struct ieee802_1x_kay_peer *peer, 2214780fb4a2SCy Schubert const struct ieee802_1x_kay_peer *other) 2215780fb4a2SCy Schubert { 2216780fb4a2SCy Schubert if (peer->key_server_priority < other->key_server_priority) 2217780fb4a2SCy Schubert return -1; 2218780fb4a2SCy Schubert if (other->key_server_priority < peer->key_server_priority) 2219780fb4a2SCy Schubert return 1; 2220780fb4a2SCy Schubert 2221780fb4a2SCy Schubert return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN); 22225b9c547cSRui Paulo } 22235b9c547cSRui Paulo 22245b9c547cSRui Paulo 22255b9c547cSRui Paulo /** 22265b9c547cSRui Paulo * ieee802_1x_kay_elect_key_server - elect the key server 22275b9c547cSRui Paulo * when to elect: whenever the live peers list changes 22285b9c547cSRui Paulo */ 22295b9c547cSRui Paulo static int 22305b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) 22315b9c547cSRui Paulo { 22325b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 22335b9c547cSRui Paulo struct ieee802_1x_kay_peer *key_server = NULL; 22345b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 22355b9c547cSRui Paulo Boolean i_is_key_server; 223685732ac8SCy Schubert int priority_comparison; 22375b9c547cSRui Paulo 22385b9c547cSRui Paulo if (participant->is_obliged_key_server) { 22395b9c547cSRui Paulo participant->new_sak = TRUE; 22405b9c547cSRui Paulo participant->to_dist_sak = FALSE; 22415b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, TRUE); 22425b9c547cSRui Paulo return 0; 22435b9c547cSRui Paulo } 22445b9c547cSRui Paulo 22455b9c547cSRui Paulo /* elect the key server among the peers */ 22465b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 22475b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 22485b9c547cSRui Paulo if (!peer->is_key_server) 22495b9c547cSRui Paulo continue; 22505b9c547cSRui Paulo 22515b9c547cSRui Paulo if (!key_server) { 22525b9c547cSRui Paulo key_server = peer; 22535b9c547cSRui Paulo continue; 22545b9c547cSRui Paulo } 22555b9c547cSRui Paulo 2256780fb4a2SCy Schubert if (compare_priorities(peer, key_server) < 0) 22575b9c547cSRui Paulo key_server = peer; 22585b9c547cSRui Paulo } 22595b9c547cSRui Paulo 22605b9c547cSRui Paulo /* elect the key server between me and the above elected peer */ 22615b9c547cSRui Paulo i_is_key_server = FALSE; 22625b9c547cSRui Paulo if (key_server && participant->can_be_key_server) { 2263780fb4a2SCy Schubert struct ieee802_1x_kay_peer tmp; 22645b9c547cSRui Paulo 2265780fb4a2SCy Schubert tmp.key_server_priority = kay->actor_priority; 2266780fb4a2SCy Schubert os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); 226785732ac8SCy Schubert priority_comparison = compare_priorities(&tmp, key_server); 226885732ac8SCy Schubert if (priority_comparison < 0) { 2269780fb4a2SCy Schubert i_is_key_server = TRUE; 227085732ac8SCy Schubert } else if (priority_comparison == 0) { 227185732ac8SCy Schubert wpa_printf(MSG_WARNING, 227285732ac8SCy Schubert "KaY: Cannot elect key server between me and peer, duplicate MAC detected"); 227385732ac8SCy Schubert key_server = NULL; 227485732ac8SCy Schubert } 2275780fb4a2SCy Schubert } else if (participant->can_be_key_server) { 2276780fb4a2SCy Schubert i_is_key_server = TRUE; 22775b9c547cSRui Paulo } 22785b9c547cSRui Paulo 22795b9c547cSRui Paulo if (i_is_key_server) { 22805b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, TRUE); 2281780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) { 22825b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp); 22835b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 22845b9c547cSRui Paulo } 22855b9c547cSRui Paulo 22865b9c547cSRui Paulo participant->is_key_server = TRUE; 22875b9c547cSRui Paulo participant->principal = TRUE; 22885b9c547cSRui Paulo participant->new_sak = TRUE; 22894bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: I am elected as key server"); 22905b9c547cSRui Paulo participant->to_dist_sak = FALSE; 22915b9c547cSRui Paulo participant->is_elected = TRUE; 22925b9c547cSRui Paulo 22935b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci, 22945b9c547cSRui Paulo sizeof(kay->key_server_sci)); 22955b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority; 2296780fb4a2SCy Schubert } else if (key_server) { 22974bc52338SCy Schubert wpa_printf(MSG_DEBUG, 22984bc52338SCy Schubert "KaY: Peer %s was elected as the key server", 22994bc52338SCy Schubert mi_txt(key_server->mi)); 23005b9c547cSRui Paulo ieee802_1x_cp_set_electedself(kay->cp, FALSE); 2301780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { 23025b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp); 23035b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 23045b9c547cSRui Paulo } 23055b9c547cSRui Paulo 23065b9c547cSRui Paulo participant->is_key_server = FALSE; 23075b9c547cSRui Paulo participant->principal = TRUE; 23085b9c547cSRui Paulo participant->is_elected = TRUE; 23095b9c547cSRui Paulo 23105b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &key_server->sci, 23115b9c547cSRui Paulo sizeof(kay->key_server_sci)); 23125b9c547cSRui Paulo kay->key_server_priority = key_server->key_server_priority; 2313780fb4a2SCy Schubert } else { 2314780fb4a2SCy Schubert participant->principal = FALSE; 2315780fb4a2SCy Schubert participant->is_key_server = FALSE; 2316780fb4a2SCy Schubert participant->is_elected = FALSE; 23175b9c547cSRui Paulo } 23185b9c547cSRui Paulo 23195b9c547cSRui Paulo return 0; 23205b9c547cSRui Paulo } 23215b9c547cSRui Paulo 23225b9c547cSRui Paulo 23235b9c547cSRui Paulo /** 23245b9c547cSRui Paulo * ieee802_1x_kay_decide_macsec_use - the key server determinate 23255b9c547cSRui Paulo * how to use MACsec: whether use MACsec and its capability 23265b9c547cSRui Paulo * protectFrames will be advised if the key server and one of its live peers are 23275b9c547cSRui Paulo * MACsec capable and one of those request MACsec protection 23285b9c547cSRui Paulo */ 23295b9c547cSRui Paulo static int 23305b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use( 23315b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 23325b9c547cSRui Paulo { 23335b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 23345b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 23355b9c547cSRui Paulo enum macsec_cap less_capability; 23365b9c547cSRui Paulo Boolean has_peer; 23375b9c547cSRui Paulo 23385b9c547cSRui Paulo if (!participant->is_key_server) 23395b9c547cSRui Paulo return -1; 23405b9c547cSRui Paulo 23415b9c547cSRui Paulo /* key server self is MACsec-desired and requesting MACsec */ 23425b9c547cSRui Paulo if (!kay->macsec_desired) { 23435b9c547cSRui Paulo participant->advised_desired = FALSE; 23445b9c547cSRui Paulo return -1; 23455b9c547cSRui Paulo } 23465b9c547cSRui Paulo if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 23475b9c547cSRui Paulo participant->advised_desired = FALSE; 23485b9c547cSRui Paulo return -1; 23495b9c547cSRui Paulo } 23505b9c547cSRui Paulo less_capability = kay->macsec_capable; 23515b9c547cSRui Paulo 23525b9c547cSRui Paulo /* at least one of peers is MACsec-desired and requesting MACsec */ 23535b9c547cSRui Paulo has_peer = FALSE; 23545b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers, 23555b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 23565b9c547cSRui Paulo if (!peer->macsec_desired) 23575b9c547cSRui Paulo continue; 23585b9c547cSRui Paulo 2359780fb4a2SCy Schubert if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED) 23605b9c547cSRui Paulo continue; 23615b9c547cSRui Paulo 2362780fb4a2SCy Schubert less_capability = (less_capability < peer->macsec_capability) ? 2363780fb4a2SCy Schubert less_capability : peer->macsec_capability; 23645b9c547cSRui Paulo has_peer = TRUE; 23655b9c547cSRui Paulo } 23665b9c547cSRui Paulo 23675b9c547cSRui Paulo if (has_peer) { 23685b9c547cSRui Paulo participant->advised_desired = TRUE; 23695b9c547cSRui Paulo participant->advised_capability = less_capability; 23705b9c547cSRui Paulo kay->authenticated = FALSE; 23715b9c547cSRui Paulo kay->secured = TRUE; 23725b9c547cSRui Paulo kay->failed = FALSE; 23735b9c547cSRui Paulo ieee802_1x_cp_connect_secure(kay->cp); 23745b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 23755b9c547cSRui Paulo } else { 23765b9c547cSRui Paulo participant->advised_desired = FALSE; 23775b9c547cSRui Paulo participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED; 23785b9c547cSRui Paulo participant->to_use_sak = FALSE; 23795b9c547cSRui Paulo kay->authenticated = TRUE; 23805b9c547cSRui Paulo kay->secured = FALSE; 23815b9c547cSRui Paulo kay->failed = FALSE; 23825b9c547cSRui Paulo kay->ltx_kn = 0; 23835b9c547cSRui Paulo kay->ltx_an = 0; 23845b9c547cSRui Paulo kay->lrx_kn = 0; 23855b9c547cSRui Paulo kay->lrx_an = 0; 23865b9c547cSRui Paulo kay->otx_kn = 0; 23875b9c547cSRui Paulo kay->otx_an = 0; 23885b9c547cSRui Paulo kay->orx_kn = 0; 23895b9c547cSRui Paulo kay->orx_an = 0; 23905b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp); 23915b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 23925b9c547cSRui Paulo } 23935b9c547cSRui Paulo 23945b9c547cSRui Paulo return 0; 23955b9c547cSRui Paulo } 23965b9c547cSRui Paulo 23975b9c547cSRui Paulo static const u8 pae_group_addr[ETH_ALEN] = { 23985b9c547cSRui Paulo 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 23995b9c547cSRui Paulo }; 24005b9c547cSRui Paulo 24015b9c547cSRui Paulo 24025b9c547cSRui Paulo /** 24035b9c547cSRui Paulo * ieee802_1x_kay_encode_mkpdu - 24045b9c547cSRui Paulo */ 24055b9c547cSRui Paulo static int 24065b9c547cSRui Paulo ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, 24075b9c547cSRui Paulo struct wpabuf *pbuf) 24085b9c547cSRui Paulo { 24095b9c547cSRui Paulo unsigned int i; 24105b9c547cSRui Paulo struct ieee8023_hdr *ether_hdr; 24115b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 24125b9c547cSRui Paulo 24135b9c547cSRui Paulo ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr)); 24145b9c547cSRui Paulo os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest)); 24155b9c547cSRui Paulo os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr, 24165b9c547cSRui Paulo sizeof(ether_hdr->dest)); 24175b9c547cSRui Paulo ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL); 24184bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR 24194bc52338SCy Schubert " Ethertype=0x%x", 24204bc52338SCy Schubert MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src), 24214bc52338SCy Schubert be_to_host16(ether_hdr->ethertype)); 24225b9c547cSRui Paulo 24235b9c547cSRui Paulo eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr)); 24245b9c547cSRui Paulo eapol_hdr->version = EAPOL_VERSION; 24255b9c547cSRui Paulo eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; 24264bc52338SCy Schubert eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf)); 24274bc52338SCy Schubert wpa_printf(MSG_DEBUG, 24284bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", 24294bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type, 24304bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 24315b9c547cSRui Paulo 2432780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { 2433780fb4a2SCy Schubert if (mka_body_handler[i].body_present && 2434780fb4a2SCy Schubert mka_body_handler[i].body_present(participant)) { 2435780fb4a2SCy Schubert if (mka_body_handler[i].body_tx(participant, pbuf)) 24365b9c547cSRui Paulo return -1; 24375b9c547cSRui Paulo } 24385b9c547cSRui Paulo } 24395b9c547cSRui Paulo 24405b9c547cSRui Paulo return 0; 24415b9c547cSRui Paulo } 24425b9c547cSRui Paulo 24434bc52338SCy Schubert 24445b9c547cSRui Paulo /** 24455b9c547cSRui Paulo * ieee802_1x_participant_send_mkpdu - 24465b9c547cSRui Paulo */ 24475b9c547cSRui Paulo static int 24485b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu( 24495b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant) 24505b9c547cSRui Paulo { 24515b9c547cSRui Paulo struct wpabuf *buf; 24525b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay; 24535b9c547cSRui Paulo size_t length = 0; 24545b9c547cSRui Paulo unsigned int i; 24555b9c547cSRui Paulo 24564bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)", 24574bc52338SCy Schubert kay->if_name); 24585b9c547cSRui Paulo length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); 2459780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { 2460780fb4a2SCy Schubert if (mka_body_handler[i].body_present && 2461780fb4a2SCy Schubert mka_body_handler[i].body_present(participant)) 2462780fb4a2SCy Schubert length += mka_body_handler[i].body_length(participant); 24635b9c547cSRui Paulo } 24645b9c547cSRui Paulo 24655b9c547cSRui Paulo buf = wpabuf_alloc(length); 24665b9c547cSRui Paulo if (!buf) { 24675b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: out of memory"); 24685b9c547cSRui Paulo return -1; 24695b9c547cSRui Paulo } 24705b9c547cSRui Paulo 24715b9c547cSRui Paulo if (ieee802_1x_kay_encode_mkpdu(participant, buf)) { 24724bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail"); 24735b9c547cSRui Paulo return -1; 24745b9c547cSRui Paulo } 24755b9c547cSRui Paulo 24764bc52338SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf); 24775b9c547cSRui Paulo l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf)); 24785b9c547cSRui Paulo wpabuf_free(buf); 24795b9c547cSRui Paulo 24805b9c547cSRui Paulo kay->active = TRUE; 24815b9c547cSRui Paulo participant->active = TRUE; 24825b9c547cSRui Paulo 24835b9c547cSRui Paulo return 0; 24845b9c547cSRui Paulo } 24855b9c547cSRui Paulo 24865b9c547cSRui Paulo 24875b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa); 248885732ac8SCy Schubert 248985732ac8SCy Schubert static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay, 249085732ac8SCy Schubert struct transmit_sa *sa) 249185732ac8SCy Schubert { 249285732ac8SCy Schubert secy_disable_transmit_sa(kay, sa); 249385732ac8SCy Schubert secy_delete_transmit_sa(kay, sa); 249485732ac8SCy Schubert ieee802_1x_kay_deinit_transmit_sa(sa); 249585732ac8SCy Schubert } 249685732ac8SCy Schubert 249785732ac8SCy Schubert 24985b9c547cSRui Paulo /** 24995b9c547cSRui Paulo * ieee802_1x_participant_timer - 25005b9c547cSRui Paulo */ 25015b9c547cSRui Paulo static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) 25025b9c547cSRui Paulo { 25035b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 25045b9c547cSRui Paulo struct ieee802_1x_kay *kay; 25055b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer, *pre_peer; 25065b9c547cSRui Paulo time_t now = time(NULL); 25075b9c547cSRui Paulo Boolean lp_changed; 25085b9c547cSRui Paulo struct receive_sc *rxsc, *pre_rxsc; 25095b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa; 25105b9c547cSRui Paulo 25115b9c547cSRui Paulo participant = (struct ieee802_1x_mka_participant *)eloop_ctx; 25125b9c547cSRui Paulo kay = participant->kay; 25134bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)", 25144bc52338SCy Schubert kay->if_name); 25155b9c547cSRui Paulo if (participant->cak_life) { 2516780fb4a2SCy Schubert if (now > participant->cak_life) 2517780fb4a2SCy Schubert goto delete_mka; 25185b9c547cSRui Paulo } 25195b9c547cSRui Paulo 25205b9c547cSRui Paulo /* should delete MKA instance if there are not live peers 25215b9c547cSRui Paulo * when the MKA life elapsed since its creating */ 25225b9c547cSRui Paulo if (participant->mka_life) { 25235b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 2524780fb4a2SCy Schubert if (now > participant->mka_life) 2525780fb4a2SCy Schubert goto delete_mka; 25265b9c547cSRui Paulo } else { 25275b9c547cSRui Paulo participant->mka_life = 0; 25285b9c547cSRui Paulo } 25295b9c547cSRui Paulo } 25305b9c547cSRui Paulo 25315b9c547cSRui Paulo lp_changed = FALSE; 25325b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->live_peers, 25335b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 25345b9c547cSRui Paulo if (now > peer->expire) { 25355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Live peer removed"); 25365b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, 25375b9c547cSRui Paulo sizeof(peer->mi)); 25385b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); 25395b9c547cSRui Paulo dl_list_for_each_safe(rxsc, pre_rxsc, 25405b9c547cSRui Paulo &participant->rxsc_list, 25415b9c547cSRui Paulo struct receive_sc, list) { 2542780fb4a2SCy Schubert if (sci_equal(&rxsc->sci, &peer->sci)) { 25435b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc( 25445b9c547cSRui Paulo participant, rxsc); 25455b9c547cSRui Paulo } 25465b9c547cSRui Paulo } 25475b9c547cSRui Paulo dl_list_del(&peer->list); 25485b9c547cSRui Paulo os_free(peer); 25495b9c547cSRui Paulo lp_changed = TRUE; 25505b9c547cSRui Paulo } 25515b9c547cSRui Paulo } 25525b9c547cSRui Paulo 25535b9c547cSRui Paulo if (lp_changed) { 25545b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) { 25555b9c547cSRui Paulo participant->advised_desired = FALSE; 25565b9c547cSRui Paulo participant->advised_capability = 25575b9c547cSRui Paulo MACSEC_CAP_NOT_IMPLEMENTED; 25585b9c547cSRui Paulo participant->to_use_sak = FALSE; 255985732ac8SCy Schubert participant->ltx = FALSE; 256085732ac8SCy Schubert participant->lrx = FALSE; 256185732ac8SCy Schubert participant->otx = FALSE; 256285732ac8SCy Schubert participant->orx = FALSE; 256385732ac8SCy Schubert participant->is_key_server = FALSE; 256485732ac8SCy Schubert participant->is_elected = FALSE; 256585732ac8SCy Schubert kay->authenticated = FALSE; 25665b9c547cSRui Paulo kay->secured = FALSE; 25675b9c547cSRui Paulo kay->failed = FALSE; 25685b9c547cSRui Paulo kay->ltx_kn = 0; 25695b9c547cSRui Paulo kay->ltx_an = 0; 25705b9c547cSRui Paulo kay->lrx_kn = 0; 25715b9c547cSRui Paulo kay->lrx_an = 0; 25725b9c547cSRui Paulo kay->otx_kn = 0; 25735b9c547cSRui Paulo kay->otx_an = 0; 25745b9c547cSRui Paulo kay->orx_kn = 0; 25755b9c547cSRui Paulo kay->orx_an = 0; 25765b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa, 25775b9c547cSRui Paulo &participant->txsc->sa_list, 25785b9c547cSRui Paulo struct transmit_sa, list) { 257985732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 25805b9c547cSRui Paulo } 25815b9c547cSRui Paulo 258285732ac8SCy Schubert ieee802_1x_cp_connect_pending(kay->cp); 25835b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 25845b9c547cSRui Paulo } else { 25855b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant); 25865b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant); 25875b9c547cSRui Paulo } 25885b9c547cSRui Paulo } 25895b9c547cSRui Paulo 25905b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers, 25915b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) { 25925b9c547cSRui Paulo if (now > peer->expire) { 25935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Potential peer removed"); 25945b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, 25955b9c547cSRui Paulo sizeof(peer->mi)); 25965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); 25975b9c547cSRui Paulo dl_list_del(&peer->list); 25985b9c547cSRui Paulo os_free(peer); 25995b9c547cSRui Paulo } 26005b9c547cSRui Paulo } 26015b9c547cSRui Paulo 26024bc52338SCy Schubert if (participant->new_sak && participant->is_key_server) { 26035b9c547cSRui Paulo if (!ieee802_1x_kay_generate_new_sak(participant)) 26045b9c547cSRui Paulo participant->to_dist_sak = TRUE; 26055b9c547cSRui Paulo 26065b9c547cSRui Paulo participant->new_sak = FALSE; 26075b9c547cSRui Paulo } 26085b9c547cSRui Paulo 260985732ac8SCy Schubert if (participant->retry_count < MAX_RETRY_CNT || 261085732ac8SCy Schubert participant->mode == PSK) { 26115b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(participant); 26125b9c547cSRui Paulo participant->retry_count++; 26135b9c547cSRui Paulo } 26145b9c547cSRui Paulo 26154bc52338SCy Schubert eloop_register_timeout(kay->mka_hello_time / 1000, 0, 26165b9c547cSRui Paulo ieee802_1x_participant_timer, 26175b9c547cSRui Paulo participant, NULL); 2618780fb4a2SCy Schubert 2619780fb4a2SCy Schubert return; 2620780fb4a2SCy Schubert 2621780fb4a2SCy Schubert delete_mka: 2622780fb4a2SCy Schubert kay->authenticated = FALSE; 2623780fb4a2SCy Schubert kay->secured = FALSE; 2624780fb4a2SCy Schubert kay->failed = TRUE; 2625780fb4a2SCy Schubert ieee802_1x_kay_delete_mka(kay, &participant->ckn); 26265b9c547cSRui Paulo } 26275b9c547cSRui Paulo 26285b9c547cSRui Paulo 26295b9c547cSRui Paulo /** 26305b9c547cSRui Paulo * ieee802_1x_kay_init_transmit_sa - 26315b9c547cSRui Paulo */ 26325b9c547cSRui Paulo static struct transmit_sa * 26335b9c547cSRui Paulo ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, 26345b9c547cSRui Paulo struct data_key *key) 26355b9c547cSRui Paulo { 26365b9c547cSRui Paulo struct transmit_sa *psa; 26375b9c547cSRui Paulo 26385b9c547cSRui Paulo key->tx_latest = TRUE; 26395b9c547cSRui Paulo key->rx_latest = TRUE; 26405b9c547cSRui Paulo 26415b9c547cSRui Paulo psa = os_zalloc(sizeof(*psa)); 26425b9c547cSRui Paulo if (!psa) { 26435b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 26445b9c547cSRui Paulo return NULL; 26455b9c547cSRui Paulo } 26465b9c547cSRui Paulo 26475b9c547cSRui Paulo if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 && 26485b9c547cSRui Paulo key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50) 26495b9c547cSRui Paulo psa->confidentiality = TRUE; 26505b9c547cSRui Paulo else 26515b9c547cSRui Paulo psa->confidentiality = FALSE; 26525b9c547cSRui Paulo 26535b9c547cSRui Paulo psa->an = an; 265485732ac8SCy Schubert ieee802_1x_kay_use_data_key(key); 26555b9c547cSRui Paulo psa->pkey = key; 26565b9c547cSRui Paulo psa->next_pn = next_PN; 26575b9c547cSRui Paulo psa->sc = psc; 26585b9c547cSRui Paulo 26595b9c547cSRui Paulo os_get_time(&psa->created_time); 26605b9c547cSRui Paulo psa->in_use = FALSE; 26615b9c547cSRui Paulo 26625b9c547cSRui Paulo dl_list_add(&psc->sa_list, &psa->list); 26635b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 26644bc52338SCy Schubert "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC", 266585732ac8SCy Schubert an, next_PN); 26665b9c547cSRui Paulo 26675b9c547cSRui Paulo return psa; 26685b9c547cSRui Paulo } 26695b9c547cSRui Paulo 26705b9c547cSRui Paulo 26715b9c547cSRui Paulo /** 26725b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sa - 26735b9c547cSRui Paulo */ 26745b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) 26755b9c547cSRui Paulo { 267685732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(psa->pkey); 26775b9c547cSRui Paulo psa->pkey = NULL; 26785b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 2679780fb4a2SCy Schubert "KaY: Delete transmit SA(an: %hhu) of SC", 2680780fb4a2SCy Schubert psa->an); 26815b9c547cSRui Paulo dl_list_del(&psa->list); 26825b9c547cSRui Paulo os_free(psa); 26835b9c547cSRui Paulo } 26845b9c547cSRui Paulo 26855b9c547cSRui Paulo 26865b9c547cSRui Paulo /** 26875b9c547cSRui Paulo * init_transmit_sc - 26885b9c547cSRui Paulo */ 26895b9c547cSRui Paulo static struct transmit_sc * 269085732ac8SCy Schubert ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) 26915b9c547cSRui Paulo { 26925b9c547cSRui Paulo struct transmit_sc *psc; 26935b9c547cSRui Paulo 26945b9c547cSRui Paulo psc = os_zalloc(sizeof(*psc)); 26955b9c547cSRui Paulo if (!psc) { 26965b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__); 26975b9c547cSRui Paulo return NULL; 26985b9c547cSRui Paulo } 26995b9c547cSRui Paulo os_memcpy(&psc->sci, sci, sizeof(psc->sci)); 27005b9c547cSRui Paulo 27015b9c547cSRui Paulo os_get_time(&psc->created_time); 27025b9c547cSRui Paulo psc->transmitting = FALSE; 27035b9c547cSRui Paulo psc->encoding_sa = FALSE; 27045b9c547cSRui Paulo psc->enciphering_sa = FALSE; 27055b9c547cSRui Paulo 27065b9c547cSRui Paulo dl_list_init(&psc->sa_list); 27074bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s", 27084bc52338SCy Schubert sci_txt(&psc->sci)); 27095b9c547cSRui Paulo 27105b9c547cSRui Paulo return psc; 27115b9c547cSRui Paulo } 27125b9c547cSRui Paulo 27135b9c547cSRui Paulo 27145b9c547cSRui Paulo /** 27155b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sc - 27165b9c547cSRui Paulo */ 27175b9c547cSRui Paulo static void 27185b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc( 27195b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc) 27205b9c547cSRui Paulo { 27215b9c547cSRui Paulo struct transmit_sa *psa, *tmp; 27225b9c547cSRui Paulo 272385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC"); 272485732ac8SCy Schubert dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list) 272585732ac8SCy Schubert ieee802_1x_delete_transmit_sa(participant->kay, psa); 27265b9c547cSRui Paulo 272785732ac8SCy Schubert secy_delete_transmit_sc(participant->kay, psc); 27285b9c547cSRui Paulo os_free(psc); 27295b9c547cSRui Paulo } 27305b9c547cSRui Paulo 27315b9c547cSRui Paulo 27325b9c547cSRui Paulo /****************** Interface between CP and KAY *********************/ 27335b9c547cSRui Paulo /** 27345b9c547cSRui Paulo * ieee802_1x_kay_set_latest_sa_attr - 27355b9c547cSRui Paulo */ 27365b9c547cSRui Paulo int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, 27375b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki, u8 lan, 27385b9c547cSRui Paulo Boolean ltx, Boolean lrx) 27395b9c547cSRui Paulo { 27405b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 27415b9c547cSRui Paulo 27425b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 27435b9c547cSRui Paulo if (!principal) 27445b9c547cSRui Paulo return -1; 27455b9c547cSRui Paulo 27465b9c547cSRui Paulo if (!lki) 27475b9c547cSRui Paulo os_memset(&principal->lki, 0, sizeof(principal->lki)); 27485b9c547cSRui Paulo else 27495b9c547cSRui Paulo os_memcpy(&principal->lki, lki, sizeof(principal->lki)); 27505b9c547cSRui Paulo 27515b9c547cSRui Paulo principal->lan = lan; 27525b9c547cSRui Paulo principal->ltx = ltx; 27535b9c547cSRui Paulo principal->lrx = lrx; 27545b9c547cSRui Paulo if (!lki) { 27555b9c547cSRui Paulo kay->ltx_kn = 0; 27565b9c547cSRui Paulo kay->lrx_kn = 0; 27575b9c547cSRui Paulo } else { 27585b9c547cSRui Paulo kay->ltx_kn = lki->kn; 27595b9c547cSRui Paulo kay->lrx_kn = lki->kn; 27605b9c547cSRui Paulo } 27615b9c547cSRui Paulo kay->ltx_an = lan; 27625b9c547cSRui Paulo kay->lrx_an = lan; 27635b9c547cSRui Paulo 27645b9c547cSRui Paulo return 0; 27655b9c547cSRui Paulo } 27665b9c547cSRui Paulo 27675b9c547cSRui Paulo 27685b9c547cSRui Paulo /** 27695b9c547cSRui Paulo * ieee802_1x_kay_set_old_sa_attr - 27705b9c547cSRui Paulo */ 27715b9c547cSRui Paulo int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, 27725b9c547cSRui Paulo struct ieee802_1x_mka_ki *oki, 27735b9c547cSRui Paulo u8 oan, Boolean otx, Boolean orx) 27745b9c547cSRui Paulo { 27755b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 27765b9c547cSRui Paulo 27775b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 27785b9c547cSRui Paulo if (!principal) 27795b9c547cSRui Paulo return -1; 27805b9c547cSRui Paulo 27815b9c547cSRui Paulo if (!oki) 27825b9c547cSRui Paulo os_memset(&principal->oki, 0, sizeof(principal->oki)); 27835b9c547cSRui Paulo else 27845b9c547cSRui Paulo os_memcpy(&principal->oki, oki, sizeof(principal->oki)); 27855b9c547cSRui Paulo 27865b9c547cSRui Paulo principal->oan = oan; 27875b9c547cSRui Paulo principal->otx = otx; 27885b9c547cSRui Paulo principal->orx = orx; 27895b9c547cSRui Paulo 27905b9c547cSRui Paulo if (!oki) { 27915b9c547cSRui Paulo kay->otx_kn = 0; 27925b9c547cSRui Paulo kay->orx_kn = 0; 27935b9c547cSRui Paulo } else { 27945b9c547cSRui Paulo kay->otx_kn = oki->kn; 27955b9c547cSRui Paulo kay->orx_kn = oki->kn; 27965b9c547cSRui Paulo } 27975b9c547cSRui Paulo kay->otx_an = oan; 27985b9c547cSRui Paulo kay->orx_an = oan; 27995b9c547cSRui Paulo 28005b9c547cSRui Paulo return 0; 28015b9c547cSRui Paulo } 28025b9c547cSRui Paulo 28035b9c547cSRui Paulo 280485732ac8SCy Schubert static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an) 280585732ac8SCy Schubert { 280685732ac8SCy Schubert struct transmit_sa *txsa; 280785732ac8SCy Schubert 280885732ac8SCy Schubert dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) { 280985732ac8SCy Schubert if (txsa->an == an) 281085732ac8SCy Schubert return txsa; 281185732ac8SCy Schubert } 281285732ac8SCy Schubert 281385732ac8SCy Schubert return NULL; 281485732ac8SCy Schubert } 281585732ac8SCy Schubert 281685732ac8SCy Schubert 281785732ac8SCy Schubert static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an) 281885732ac8SCy Schubert { 281985732ac8SCy Schubert struct receive_sa *rxsa; 282085732ac8SCy Schubert 282185732ac8SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) { 282285732ac8SCy Schubert if (rxsa->an == an) 282385732ac8SCy Schubert return rxsa; 282485732ac8SCy Schubert } 282585732ac8SCy Schubert 282685732ac8SCy Schubert return NULL; 282785732ac8SCy Schubert } 282885732ac8SCy Schubert 282985732ac8SCy Schubert 28305b9c547cSRui Paulo /** 28315b9c547cSRui Paulo * ieee802_1x_kay_create_sas - 28325b9c547cSRui Paulo */ 28335b9c547cSRui Paulo int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, 28345b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 28355b9c547cSRui Paulo { 28365b9c547cSRui Paulo struct data_key *sa_key, *latest_sak; 28375b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 28385b9c547cSRui Paulo struct receive_sc *rxsc; 28395b9c547cSRui Paulo struct receive_sa *rxsa; 28405b9c547cSRui Paulo struct transmit_sa *txsa; 28415b9c547cSRui Paulo 28425b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 28435b9c547cSRui Paulo if (!principal) 28445b9c547cSRui Paulo return -1; 28455b9c547cSRui Paulo 28465b9c547cSRui Paulo latest_sak = NULL; 28475b9c547cSRui Paulo dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) { 28485b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, lki)) { 28495b9c547cSRui Paulo sa_key->rx_latest = TRUE; 28505b9c547cSRui Paulo sa_key->tx_latest = TRUE; 28515b9c547cSRui Paulo latest_sak = sa_key; 28525b9c547cSRui Paulo principal->to_use_sak = TRUE; 28535b9c547cSRui Paulo } else { 28545b9c547cSRui Paulo sa_key->rx_latest = FALSE; 28555b9c547cSRui Paulo sa_key->tx_latest = FALSE; 28565b9c547cSRui Paulo } 28575b9c547cSRui Paulo } 28585b9c547cSRui Paulo if (!latest_sak) { 28594bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: lki related sak not found"); 28605b9c547cSRui Paulo return -1; 28615b9c547cSRui Paulo } 28625b9c547cSRui Paulo 28635b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 286485732ac8SCy Schubert while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL) 286585732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa); 286685732ac8SCy Schubert 28675b9c547cSRui Paulo rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1, 28685b9c547cSRui Paulo latest_sak); 28695b9c547cSRui Paulo if (!rxsa) 28705b9c547cSRui Paulo return -1; 28715b9c547cSRui Paulo 28725b9c547cSRui Paulo secy_create_receive_sa(kay, rxsa); 28735b9c547cSRui Paulo } 28745b9c547cSRui Paulo 287585732ac8SCy Schubert while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) != 287685732ac8SCy Schubert NULL) 287785732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 287885732ac8SCy Schubert 28795b9c547cSRui Paulo txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, 28804bc52338SCy Schubert latest_sak->next_pn ? 28814bc52338SCy Schubert latest_sak->next_pn : 1, 28824bc52338SCy Schubert latest_sak); 28835b9c547cSRui Paulo if (!txsa) 28845b9c547cSRui Paulo return -1; 28855b9c547cSRui Paulo 28865b9c547cSRui Paulo secy_create_transmit_sa(kay, txsa); 28875b9c547cSRui Paulo 28885b9c547cSRui Paulo 28895b9c547cSRui Paulo 28905b9c547cSRui Paulo return 0; 28915b9c547cSRui Paulo } 28925b9c547cSRui Paulo 28935b9c547cSRui Paulo 28945b9c547cSRui Paulo /** 28955b9c547cSRui Paulo * ieee802_1x_kay_delete_sas - 28965b9c547cSRui Paulo */ 28975b9c547cSRui Paulo int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, 28985b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki) 28995b9c547cSRui Paulo { 29005b9c547cSRui Paulo struct data_key *sa_key, *pre_key; 29015b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa; 29025b9c547cSRui Paulo struct receive_sa *rxsa, *pre_rxsa; 29035b9c547cSRui Paulo struct receive_sc *rxsc; 29045b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29055b9c547cSRui Paulo 29065b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__); 29075b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 29085b9c547cSRui Paulo if (!principal) 29095b9c547cSRui Paulo return -1; 29105b9c547cSRui Paulo 29115b9c547cSRui Paulo /* remove the transmit sa */ 29125b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list, 29135b9c547cSRui Paulo struct transmit_sa, list) { 291485732ac8SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki)) 291585732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa); 29165b9c547cSRui Paulo } 29175b9c547cSRui Paulo 29185b9c547cSRui Paulo /* remove the receive sa */ 29195b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 29205b9c547cSRui Paulo dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list, 29215b9c547cSRui Paulo struct receive_sa, list) { 292285732ac8SCy Schubert if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) 292385732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa); 29245b9c547cSRui Paulo } 29255b9c547cSRui Paulo } 29265b9c547cSRui Paulo 29275b9c547cSRui Paulo /* remove the sak */ 29285b9c547cSRui Paulo dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, 29295b9c547cSRui Paulo struct data_key, list) { 29305b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, ki)) { 29314bc52338SCy Schubert if (principal->new_key == sa_key) 29324bc52338SCy Schubert principal->new_key = NULL; 293385732ac8SCy Schubert dl_list_del(&sa_key->list); 29345b9c547cSRui Paulo ieee802_1x_kay_deinit_data_key(sa_key); 29355b9c547cSRui Paulo break; 29365b9c547cSRui Paulo } 29375b9c547cSRui Paulo } 29385b9c547cSRui Paulo 29395b9c547cSRui Paulo return 0; 29405b9c547cSRui Paulo } 29415b9c547cSRui Paulo 29425b9c547cSRui Paulo 29435b9c547cSRui Paulo /** 29445b9c547cSRui Paulo * ieee802_1x_kay_enable_tx_sas - 29455b9c547cSRui Paulo */ 29465b9c547cSRui Paulo int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, 29475b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 29485b9c547cSRui Paulo { 29495b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29505b9c547cSRui Paulo struct transmit_sa *txsa; 29515b9c547cSRui Paulo 29525b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 29535b9c547cSRui Paulo if (!principal) 29545b9c547cSRui Paulo return -1; 29555b9c547cSRui Paulo 29565b9c547cSRui Paulo dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa, 29575b9c547cSRui Paulo list) { 29585b9c547cSRui Paulo if (is_ki_equal(&txsa->pkey->key_identifier, lki)) { 29595b9c547cSRui Paulo txsa->in_use = TRUE; 29605b9c547cSRui Paulo secy_enable_transmit_sa(kay, txsa); 29615b9c547cSRui Paulo ieee802_1x_cp_set_usingtransmitas( 29625b9c547cSRui Paulo principal->kay->cp, TRUE); 29635b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp); 29645b9c547cSRui Paulo } 29655b9c547cSRui Paulo } 29665b9c547cSRui Paulo 29675b9c547cSRui Paulo return 0; 29685b9c547cSRui Paulo } 29695b9c547cSRui Paulo 29705b9c547cSRui Paulo 29715b9c547cSRui Paulo /** 29725b9c547cSRui Paulo * ieee802_1x_kay_enable_rx_sas - 29735b9c547cSRui Paulo */ 29745b9c547cSRui Paulo int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, 29755b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki) 29765b9c547cSRui Paulo { 29775b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 29785b9c547cSRui Paulo struct receive_sa *rxsa; 29795b9c547cSRui Paulo struct receive_sc *rxsc; 29805b9c547cSRui Paulo 29815b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 29825b9c547cSRui Paulo if (!principal) 29835b9c547cSRui Paulo return -1; 29845b9c547cSRui Paulo 29855b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { 29865b9c547cSRui Paulo dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) 29875b9c547cSRui Paulo { 29885b9c547cSRui Paulo if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) { 29895b9c547cSRui Paulo rxsa->in_use = TRUE; 29905b9c547cSRui Paulo secy_enable_receive_sa(kay, rxsa); 29915b9c547cSRui Paulo ieee802_1x_cp_set_usingreceivesas( 29925b9c547cSRui Paulo principal->kay->cp, TRUE); 29935b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp); 29945b9c547cSRui Paulo } 29955b9c547cSRui Paulo } 29965b9c547cSRui Paulo } 29975b9c547cSRui Paulo 29985b9c547cSRui Paulo return 0; 29995b9c547cSRui Paulo } 30005b9c547cSRui Paulo 30015b9c547cSRui Paulo 30025b9c547cSRui Paulo /** 30035b9c547cSRui Paulo * ieee802_1x_kay_enable_new_info - 30045b9c547cSRui Paulo */ 30055b9c547cSRui Paulo int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) 30065b9c547cSRui Paulo { 30075b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal; 30085b9c547cSRui Paulo 30095b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay); 30105b9c547cSRui Paulo if (!principal) 30115b9c547cSRui Paulo return -1; 30125b9c547cSRui Paulo 301385732ac8SCy Schubert if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) { 30145b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(principal); 30155b9c547cSRui Paulo principal->retry_count++; 30165b9c547cSRui Paulo } 30175b9c547cSRui Paulo 30185b9c547cSRui Paulo return 0; 30195b9c547cSRui Paulo } 30205b9c547cSRui Paulo 30215b9c547cSRui Paulo 30225b9c547cSRui Paulo /** 30235b9c547cSRui Paulo * ieee802_1x_kay_mkpdu_sanity_check - 30244bc52338SCy Schubert * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of 30254bc52338SCy Schubert * MKPDUs) 30265b9c547cSRui Paulo */ 30275b9c547cSRui Paulo static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, 30285b9c547cSRui Paulo const u8 *buf, size_t len) 30295b9c547cSRui Paulo { 30305b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr; 30315b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 30325b9c547cSRui Paulo struct ieee802_1x_mka_hdr *mka_hdr; 30335b9c547cSRui Paulo struct ieee802_1x_mka_basic_body *body; 30345b9c547cSRui Paulo size_t mka_msg_len; 30355b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 30365b9c547cSRui Paulo size_t body_len; 303785732ac8SCy Schubert size_t ckn_len; 30385b9c547cSRui Paulo u8 icv[MAX_ICV_LEN]; 30394bc52338SCy Schubert const u8 *msg_icv; 30405b9c547cSRui Paulo 30414bc52338SCy Schubert /* len > eth+eapol header already verified in kay_l2_receive(); 30424bc52338SCy Schubert * likewise, eapol_hdr->length validated there */ 30435b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf; 30445b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); 30455b9c547cSRui Paulo mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1); 30465b9c547cSRui Paulo 30474bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR 30484bc52338SCy Schubert " Ethertype=0x%x", 30494bc52338SCy Schubert MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src), 30504bc52338SCy Schubert be_to_host16(eth_hdr->ethertype)); 30514bc52338SCy Schubert 30524bc52338SCy Schubert /* the destination address shall not be an individual address */ 30535b9c547cSRui Paulo if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) { 30544bc52338SCy Schubert wpa_printf(MSG_DEBUG, 30555b9c547cSRui Paulo "KaY: ethernet destination address is not PAE group address"); 30565b9c547cSRui Paulo return -1; 30575b9c547cSRui Paulo } 30585b9c547cSRui Paulo 30594bc52338SCy Schubert wpa_printf(MSG_DEBUG, 30604bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", 30614bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type, 30624bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 30634bc52338SCy Schubert 30644bc52338SCy Schubert /* MKPDU shall not be less than 32 octets */ 30655b9c547cSRui Paulo mka_msg_len = be_to_host16(eapol_hdr->length); 30665b9c547cSRui Paulo if (mka_msg_len < 32) { 30674bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets"); 30685b9c547cSRui Paulo return -1; 30695b9c547cSRui Paulo } 30704bc52338SCy Schubert /* MKPDU shall be a multiple of 4 octets */ 30715b9c547cSRui Paulo if ((mka_msg_len % 4) != 0) { 30724bc52338SCy Schubert wpa_printf(MSG_DEBUG, 30735b9c547cSRui Paulo "KaY: MKPDU is not multiple of 4 octets"); 30745b9c547cSRui Paulo return -1; 30755b9c547cSRui Paulo } 30765b9c547cSRui Paulo 30774bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)", 30784bc52338SCy Schubert mka_hdr, mka_msg_len); 30794bc52338SCy Schubert 30804bc52338SCy Schubert /* Room for body_len already verified in kay_l2_receive() */ 30815b9c547cSRui Paulo body = (struct ieee802_1x_mka_basic_body *) mka_hdr; 30825b9c547cSRui Paulo body_len = get_mka_param_body_len(body); 30835b9c547cSRui Paulo /* EAPOL-MKA body should comprise basic parameter set and ICV */ 30845b9c547cSRui Paulo if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { 30855b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3086780fb4a2SCy 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", 3087780fb4a2SCy Schubert mka_msg_len, MKA_HDR_LEN, 3088780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN); 30895b9c547cSRui Paulo return -1; 30905b9c547cSRui Paulo } 30915b9c547cSRui Paulo 309285732ac8SCy Schubert if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { 309385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", 309485732ac8SCy Schubert body_len); 309585732ac8SCy Schubert return -1; 309685732ac8SCy Schubert } 309785732ac8SCy Schubert ckn_len = body_len - 309885732ac8SCy Schubert (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); 309985732ac8SCy Schubert if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { 31004bc52338SCy Schubert wpa_printf(MSG_WARNING, 310185732ac8SCy Schubert "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", 310285732ac8SCy Schubert ckn_len, MAX_CKN_LEN); 310385732ac8SCy Schubert return -1; 310485732ac8SCy Schubert } 310585732ac8SCy Schubert 31064bc52338SCy Schubert ieee802_1x_mka_dump_basic_body(body); 31074bc52338SCy Schubert 31085b9c547cSRui Paulo /* CKN should be owned by I */ 310985732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); 31105b9c547cSRui Paulo if (!participant) { 31114bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA"); 31125b9c547cSRui Paulo return -1; 31135b9c547cSRui Paulo } 31145b9c547cSRui Paulo 31155b9c547cSRui Paulo /* algorithm agility check */ 31165b9c547cSRui Paulo if (os_memcmp(body->algo_agility, mka_algo_agility, 31175b9c547cSRui Paulo sizeof(body->algo_agility)) != 0) { 31184bc52338SCy Schubert wpa_printf(MSG_INFO, 31194bc52338SCy Schubert "KaY: Peer's algorithm agility (%s) not supported", 31204bc52338SCy Schubert algo_agility_txt(body->algo_agility)); 31215b9c547cSRui Paulo return -1; 31225b9c547cSRui Paulo } 31235b9c547cSRui Paulo 31245b9c547cSRui Paulo /* ICV check */ 31255b9c547cSRui Paulo /* 31265b9c547cSRui Paulo * The ICV will comprise the final octets of the packet body, whatever 31275b9c547cSRui Paulo * its size, not the fixed length 16 octets, indicated by the EAPOL 31285b9c547cSRui Paulo * packet body length. 31295b9c547cSRui Paulo */ 31304bc52338SCy Schubert if (len < mka_alg_tbl[kay->mka_algindex].icv_len || 31314bc52338SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_hash( 31324bc52338SCy Schubert participant->ick.key, participant->ick.len, 31335b9c547cSRui Paulo buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) { 31344bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV"); 31355b9c547cSRui Paulo return -1; 31365b9c547cSRui Paulo } 3137780fb4a2SCy Schubert 31384bc52338SCy Schubert msg_icv = ieee802_1x_mka_decode_icv_body(participant, 31394bc52338SCy Schubert (const u8 *) mka_hdr, 31405b9c547cSRui Paulo mka_msg_len); 3141780fb4a2SCy Schubert if (!msg_icv) { 31424bc52338SCy Schubert wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it"); 31435b9c547cSRui Paulo return -1; 31445b9c547cSRui Paulo } 31454bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Received ICV", 31464bc52338SCy Schubert msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len); 3147780fb4a2SCy Schubert if (os_memcmp_const(msg_icv, icv, 3148780fb4a2SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { 31494bc52338SCy Schubert wpa_printf(MSG_WARNING, 3150780fb4a2SCy Schubert "KaY: Computed ICV is not equal to Received ICV"); 31514bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV", 31524bc52338SCy Schubert icv, mka_alg_tbl[kay->mka_algindex].icv_len); 31535b9c547cSRui Paulo return -1; 31545b9c547cSRui Paulo } 31555b9c547cSRui Paulo 31565b9c547cSRui Paulo return 0; 31575b9c547cSRui Paulo } 31585b9c547cSRui Paulo 31595b9c547cSRui Paulo 31605b9c547cSRui Paulo /** 31615b9c547cSRui Paulo * ieee802_1x_kay_decode_mkpdu - 31625b9c547cSRui Paulo */ 31635b9c547cSRui Paulo static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, 31645b9c547cSRui Paulo const u8 *buf, size_t len) 31655b9c547cSRui Paulo { 31665b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 31675b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr; 31684bc52338SCy Schubert struct ieee802_1x_kay_peer *peer; 31695b9c547cSRui Paulo size_t body_len; 31705b9c547cSRui Paulo size_t left_len; 3171780fb4a2SCy Schubert u8 body_type; 31725b9c547cSRui Paulo int i; 31735b9c547cSRui Paulo const u8 *pos; 31745b9c547cSRui Paulo Boolean handled[256]; 31754bc52338SCy Schubert Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use 31764bc52338SCy Schubert * parameter set */ 31774bc52338SCy Schubert Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer; 31785b9c547cSRui Paulo 31794bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)", 31804bc52338SCy Schubert kay->if_name); 31815b9c547cSRui Paulo if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) 31825b9c547cSRui Paulo return -1; 31835b9c547cSRui Paulo 31845b9c547cSRui Paulo /* handle basic parameter set */ 31855b9c547cSRui Paulo pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr); 31865b9c547cSRui Paulo left_len = len - sizeof(struct ieee8023_hdr) - 31875b9c547cSRui Paulo sizeof(struct ieee802_1x_hdr); 31885b9c547cSRui Paulo participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len); 31895b9c547cSRui Paulo if (!participant) 31905b9c547cSRui Paulo return -1; 31915b9c547cSRui Paulo 31925b9c547cSRui Paulo /* to skip basic parameter set */ 31935b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos; 31944bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 31954bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN) 31964bc52338SCy Schubert return -1; 31975b9c547cSRui Paulo pos += body_len + MKA_HDR_LEN; 31985b9c547cSRui Paulo left_len -= body_len + MKA_HDR_LEN; 31995b9c547cSRui Paulo 32005b9c547cSRui Paulo /* check i am in the peer's peer list */ 32014bc52338SCy Schubert i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos, 32024bc52338SCy Schubert left_len); 32034bc52338SCy Schubert is_in_live_peer = ieee802_1x_kay_is_in_live_peer( 32044bc52338SCy Schubert participant, participant->current_peer_id.mi); 32054bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s", 32064bc52338SCy Schubert yes_no(i_in_peerlist), yes_no(is_in_live_peer)); 32074bc52338SCy Schubert if (i_in_peerlist && !is_in_live_peer) { 3208780fb4a2SCy Schubert /* accept the peer as live peer */ 32094bc52338SCy Schubert is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer( 32104bc52338SCy Schubert participant, participant->current_peer_id.mi); 32114bc52338SCy Schubert if (is_in_potential_peer) { 3212780fb4a2SCy Schubert if (!ieee802_1x_kay_move_live_peer( 3213780fb4a2SCy Schubert participant, 3214780fb4a2SCy Schubert participant->current_peer_id.mi, 3215780fb4a2SCy Schubert be_to_host32(participant-> 3216780fb4a2SCy Schubert current_peer_id.mn))) 3217780fb4a2SCy Schubert return -1; 3218780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_live_peer( 32195b9c547cSRui Paulo participant, participant->current_peer_id.mi, 3220780fb4a2SCy Schubert be_to_host32(participant-> 3221780fb4a2SCy Schubert current_peer_id.mn))) { 3222780fb4a2SCy Schubert return -1; 3223780fb4a2SCy Schubert } 3224780fb4a2SCy Schubert 32255b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant); 32265b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant); 32275b9c547cSRui Paulo } 32285b9c547cSRui Paulo 32295b9c547cSRui Paulo /* 32305b9c547cSRui Paulo * Handle other parameter set than basic parameter set. 32315b9c547cSRui Paulo * Each parameter set should be present only once. 32325b9c547cSRui Paulo */ 32335b9c547cSRui Paulo for (i = 0; i < 256; i++) 32345b9c547cSRui Paulo handled[i] = FALSE; 32355b9c547cSRui Paulo 32365b9c547cSRui Paulo handled[0] = TRUE; 3237780fb4a2SCy Schubert for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; 3238780fb4a2SCy Schubert pos += body_len + MKA_HDR_LEN, 3239780fb4a2SCy Schubert left_len -= body_len + MKA_HDR_LEN) { 32405b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos; 32414bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); 32425b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr); 32435b9c547cSRui Paulo 32445b9c547cSRui Paulo if (body_type == MKA_ICV_INDICATOR) 32455b9c547cSRui Paulo return 0; 32465b9c547cSRui Paulo 32475b9c547cSRui Paulo if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { 32485b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3249780fb4a2SCy 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", 3250780fb4a2SCy Schubert left_len, MKA_HDR_LEN, 3251780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN); 325285732ac8SCy Schubert return -1; 32535b9c547cSRui Paulo } 32545b9c547cSRui Paulo 32554bc52338SCy Schubert if (handled[body_type]) { 32564bc52338SCy Schubert wpa_printf(MSG_DEBUG, 32574bc52338SCy Schubert "KaY: Ignore duplicated body type %u", 32584bc52338SCy Schubert body_type); 3259780fb4a2SCy Schubert continue; 32604bc52338SCy Schubert } 32615b9c547cSRui Paulo 32625b9c547cSRui Paulo handled[body_type] = TRUE; 3263780fb4a2SCy Schubert if (body_type < ARRAY_SIZE(mka_body_handler) && 3264780fb4a2SCy Schubert mka_body_handler[body_type].body_rx) { 32654bc52338SCy Schubert if (mka_body_handler[body_type].body_rx 32664bc52338SCy Schubert (participant, pos, left_len) != 0) { 32674bc52338SCy Schubert /* Handle parameter set failure */ 32684bc52338SCy Schubert if (body_type != MKA_SAK_USE) { 32694bc52338SCy Schubert wpa_printf(MSG_INFO, 32704bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", 32714bc52338SCy Schubert body_type); 32724bc52338SCy Schubert return -1; 32734bc52338SCy Schubert } 32744bc52338SCy Schubert 32754bc52338SCy Schubert /* Ideally DIST-SAK should be processed before 32764bc52338SCy Schubert * SAK-USE. Unfortunately IEEE Std 802.1X-2010, 32774bc52338SCy Schubert * 11.11.3 (Encoding MKPDUs) states SAK-USE(3) 32784bc52338SCy Schubert * must always be encoded before DIST-SAK(4). 32794bc52338SCy Schubert * Rather than redesigning mka_body_handler so 32804bc52338SCy Schubert * that it somehow processes DIST-SAK before 32814bc52338SCy Schubert * SAK-USE, just ignore SAK-USE failures if 32824bc52338SCy Schubert * DIST-SAK is also present in this MKPDU. */ 32834bc52338SCy Schubert bad_sak_use = TRUE; 32844bc52338SCy Schubert } 32855b9c547cSRui Paulo } else { 32865b9c547cSRui Paulo wpa_printf(MSG_ERROR, 32874bc52338SCy Schubert "KaY: The body type %d is not supported in this MKA version %d", 32885b9c547cSRui Paulo body_type, MKA_VERSION_ID); 32895b9c547cSRui Paulo } 32905b9c547cSRui Paulo } 32915b9c547cSRui Paulo 32924bc52338SCy Schubert if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) { 32934bc52338SCy Schubert wpa_printf(MSG_INFO, 32944bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", 32954bc52338SCy Schubert MKA_SAK_USE); 32964bc52338SCy Schubert if (!reset_participant_mi(participant)) 32974bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); 32984bc52338SCy Schubert else 32994bc52338SCy Schubert wpa_printf(MSG_DEBUG, 33004bc52338SCy Schubert "KaY: Selected a new random MI: %s", 33014bc52338SCy Schubert mi_txt(participant->mi)); 33024bc52338SCy Schubert return -1; 33034bc52338SCy Schubert } 33044bc52338SCy Schubert 33054bc52338SCy Schubert /* Detect missing parameter sets */ 33064bc52338SCy Schubert peer = ieee802_1x_kay_get_live_peer(participant, 33074bc52338SCy Schubert participant->current_peer_id.mi); 33084bc52338SCy Schubert if (peer) { 33094bc52338SCy Schubert /* MKPDU is from live peer */ 33104bc52338SCy Schubert if (!handled[MKA_SAK_USE]) { 33114bc52338SCy Schubert /* Once a live peer starts sending SAK-USE, it should be 33124bc52338SCy Schubert * sent every time. */ 33134bc52338SCy Schubert if (peer->sak_used) { 33144bc52338SCy Schubert wpa_printf(MSG_INFO, 33154bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE"); 33164bc52338SCy Schubert return -1; 33174bc52338SCy Schubert } 33184bc52338SCy Schubert 33194bc52338SCy Schubert /* Live peer is probably hung if it hasn't sent SAK-USE 33204bc52338SCy Schubert * after a reasonable number of MKPDUs. Drop the MKPDU, 33214bc52338SCy Schubert * which will eventually force an timeout. */ 33224bc52338SCy Schubert if (++peer->missing_sak_use_count > 33234bc52338SCy Schubert MAX_MISSING_SAK_USE) { 33244bc52338SCy Schubert wpa_printf(MSG_INFO, 33254bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE"); 33264bc52338SCy Schubert return -1; 33274bc52338SCy Schubert } 33284bc52338SCy Schubert } else { 33294bc52338SCy Schubert peer->missing_sak_use_count = 0; 33304bc52338SCy Schubert 33314bc52338SCy Schubert /* Only update live peer watchdog after successful 33324bc52338SCy Schubert * decode of all parameter sets */ 33334bc52338SCy Schubert peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; 33344bc52338SCy Schubert } 33354bc52338SCy Schubert } else { 33364bc52338SCy Schubert /* MKPDU is from new or potential peer */ 33374bc52338SCy Schubert peer = ieee802_1x_kay_get_peer(participant, 33384bc52338SCy Schubert participant->current_peer_id.mi); 33394bc52338SCy Schubert if (!peer) { 33404bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: No peer entry found"); 33414bc52338SCy Schubert return -1; 33424bc52338SCy Schubert } 33434bc52338SCy Schubert 33444bc52338SCy Schubert /* Do not update potential peer watchdog. Per IEEE Std 33454bc52338SCy Schubert * 802.1X-2010, 9.4.3, potential peers need to show liveness by 33464bc52338SCy Schubert * including our MI/MN in their transmitted MKPDU (within 33474bc52338SCy Schubert * potential or live parameter sets). Whena potential peer does 33484bc52338SCy Schubert * include our MI/MN in an MKPDU, we respond by moving the peer 33494bc52338SCy Schubert * from 'potential_peers' to 'live_peers'. */ 33504bc52338SCy Schubert } 33514bc52338SCy Schubert 33525b9c547cSRui Paulo kay->active = TRUE; 33535b9c547cSRui Paulo participant->retry_count = 0; 33545b9c547cSRui Paulo participant->active = TRUE; 33555b9c547cSRui Paulo 33565b9c547cSRui Paulo return 0; 33575b9c547cSRui Paulo } 33585b9c547cSRui Paulo 33595b9c547cSRui Paulo 33605b9c547cSRui Paulo 33615b9c547cSRui Paulo static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, 33625b9c547cSRui Paulo size_t len) 33635b9c547cSRui Paulo { 33645b9c547cSRui Paulo struct ieee802_1x_kay *kay = ctx; 33655b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr; 33665b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr; 33674bc52338SCy Schubert size_t calc_len; 33684bc52338SCy Schubert 33694bc52338SCy Schubert /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */ 33705b9c547cSRui Paulo 33715b9c547cSRui Paulo /* must contain at least ieee8023_hdr + ieee802_1x_hdr */ 33725b9c547cSRui Paulo if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) { 33735b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)", 33745b9c547cSRui Paulo (unsigned long) len); 33755b9c547cSRui Paulo return; 33765b9c547cSRui Paulo } 33775b9c547cSRui Paulo 33785b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf; 33795b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); 33804bc52338SCy Schubert calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) + 33814bc52338SCy Schubert be_to_host16(eapol_hdr->length); 33824bc52338SCy Schubert if (len < calc_len) { 33834bc52338SCy Schubert wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)", 33845b9c547cSRui Paulo (unsigned long) len, 33854bc52338SCy Schubert (unsigned long) calc_len, 33864bc52338SCy Schubert be_to_host16(eapol_hdr->length)); 33875b9c547cSRui Paulo return; 33885b9c547cSRui Paulo } 33894bc52338SCy Schubert if (len > calc_len) { 33904bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, 33914bc52338SCy Schubert "KaY: Ignore extra octets following the Packey Body field", 33924bc52338SCy Schubert &buf[calc_len], len - calc_len); 33934bc52338SCy Schubert len = calc_len; 33944bc52338SCy Schubert } 33955b9c547cSRui Paulo 33965b9c547cSRui Paulo if (eapol_hdr->version < EAPOL_VERSION) { 33975b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA", 33985b9c547cSRui Paulo eapol_hdr->version); 33995b9c547cSRui Paulo return; 34005b9c547cSRui Paulo } 3401780fb4a2SCy Schubert if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || 34025b9c547cSRui Paulo eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) 34034bc52338SCy Schubert return; /* ignore other EAPOL types silently here */ 34045b9c547cSRui Paulo 34054bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len); 34065b9c547cSRui Paulo if (dl_list_empty(&kay->participant_list)) { 34074bc52338SCy Schubert wpa_printf(MSG_ERROR, 34084bc52338SCy Schubert "KaY: No MKA participant instance - ignore EAPOL-MKA"); 34095b9c547cSRui Paulo return; 34105b9c547cSRui Paulo } 34115b9c547cSRui Paulo 34125b9c547cSRui Paulo ieee802_1x_kay_decode_mkpdu(kay, buf, len); 34135b9c547cSRui Paulo } 34145b9c547cSRui Paulo 34155b9c547cSRui Paulo 34165b9c547cSRui Paulo /** 34175b9c547cSRui Paulo * ieee802_1x_kay_init - 34185b9c547cSRui Paulo */ 34195b9c547cSRui Paulo struct ieee802_1x_kay * 34205b9c547cSRui Paulo ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, 34214bc52338SCy Schubert Boolean macsec_replay_protect, u32 macsec_replay_window, 342285732ac8SCy Schubert u16 port, u8 priority, const char *ifname, const u8 *addr) 34235b9c547cSRui Paulo { 34245b9c547cSRui Paulo struct ieee802_1x_kay *kay; 34255b9c547cSRui Paulo 34264bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR 34274bc52338SCy Schubert " port=%u priority=%u", 34284bc52338SCy Schubert ifname, MAC2STR(addr), port, priority); 34295b9c547cSRui Paulo kay = os_zalloc(sizeof(*kay)); 34305b9c547cSRui Paulo if (!kay) { 34315b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); 343285732ac8SCy Schubert os_free(ctx); 34335b9c547cSRui Paulo return NULL; 34345b9c547cSRui Paulo } 34355b9c547cSRui Paulo 34365b9c547cSRui Paulo kay->ctx = ctx; 34375b9c547cSRui Paulo 34385b9c547cSRui Paulo kay->enable = TRUE; 34395b9c547cSRui Paulo kay->active = FALSE; 34405b9c547cSRui Paulo 34415b9c547cSRui Paulo kay->authenticated = FALSE; 34425b9c547cSRui Paulo kay->secured = FALSE; 34435b9c547cSRui Paulo kay->failed = FALSE; 34445b9c547cSRui Paulo kay->policy = policy; 34455b9c547cSRui Paulo 34465b9c547cSRui Paulo os_strlcpy(kay->if_name, ifname, IFNAMSIZ); 34475b9c547cSRui Paulo os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); 344885732ac8SCy Schubert kay->actor_sci.port = host_to_be16(port ? port : 0x0001); 34494bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s", 34504bc52338SCy Schubert sci_txt(&kay->actor_sci)); 345185732ac8SCy Schubert kay->actor_priority = priority; 34525b9c547cSRui Paulo 34535b9c547cSRui Paulo /* While actor acts as a key server, shall distribute sakey */ 34545b9c547cSRui Paulo kay->dist_kn = 1; 34555b9c547cSRui Paulo kay->dist_an = 0; 34565b9c547cSRui Paulo kay->dist_time = 0; 34575b9c547cSRui Paulo 34585b9c547cSRui Paulo kay->pn_exhaustion = PENDING_PN_EXHAUSTION; 34595b9c547cSRui Paulo kay->macsec_csindex = DEFAULT_CS_INDEX; 34605b9c547cSRui Paulo kay->mka_algindex = DEFAULT_MKA_ALG_INDEX; 34615b9c547cSRui Paulo kay->mka_version = MKA_VERSION_ID; 34625b9c547cSRui Paulo 34635b9c547cSRui Paulo os_memcpy(kay->algo_agility, mka_algo_agility, 34645b9c547cSRui Paulo sizeof(kay->algo_agility)); 34655b9c547cSRui Paulo 34665b9c547cSRui Paulo dl_list_init(&kay->participant_list); 34675b9c547cSRui Paulo 346885732ac8SCy Schubert if (policy != DO_NOT_SECURE && 346985732ac8SCy Schubert secy_get_capability(kay, &kay->macsec_capable) < 0) 347085732ac8SCy Schubert goto error; 347185732ac8SCy Schubert 347285732ac8SCy Schubert if (policy == DO_NOT_SECURE || 347385732ac8SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { 34745b9c547cSRui Paulo kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; 34755b9c547cSRui Paulo kay->macsec_desired = FALSE; 34765b9c547cSRui Paulo kay->macsec_protect = FALSE; 34774bc52338SCy Schubert kay->macsec_encrypt = FALSE; 34785b9c547cSRui Paulo kay->macsec_validate = Disabled; 34795b9c547cSRui Paulo kay->macsec_replay_protect = FALSE; 34805b9c547cSRui Paulo kay->macsec_replay_window = 0; 34815b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_NONE; 34824bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME; 34835b9c547cSRui Paulo } else { 34845b9c547cSRui Paulo kay->macsec_desired = TRUE; 34855b9c547cSRui Paulo kay->macsec_protect = TRUE; 34864bc52338SCy Schubert if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF && 34874bc52338SCy Schubert policy == SHOULD_ENCRYPT) { 34884bc52338SCy Schubert kay->macsec_encrypt = TRUE; 34895b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; 34904bc52338SCy Schubert } else { /* SHOULD_SECURE */ 34914bc52338SCy Schubert kay->macsec_encrypt = FALSE; 349285732ac8SCy Schubert kay->macsec_confidentiality = CONFIDENTIALITY_NONE; 34935b9c547cSRui Paulo } 34944bc52338SCy Schubert kay->macsec_validate = Strict; 34954bc52338SCy Schubert kay->macsec_replay_protect = macsec_replay_protect; 34964bc52338SCy Schubert kay->macsec_replay_window = macsec_replay_window; 34974bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME; 34984bc52338SCy Schubert } 34995b9c547cSRui Paulo 35005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine created"); 35015b9c547cSRui Paulo 35025b9c547cSRui Paulo /* Initialize the SecY must be prio to CP, as CP will control SecY */ 350385732ac8SCy Schubert if (secy_init_macsec(kay) < 0) { 350485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec"); 350585732ac8SCy Schubert goto error; 350685732ac8SCy Schubert } 35075b9c547cSRui Paulo 35085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); 35095b9c547cSRui Paulo 35105b9c547cSRui Paulo /* init CP */ 3511780fb4a2SCy Schubert kay->cp = ieee802_1x_cp_sm_init(kay); 351285732ac8SCy Schubert if (kay->cp == NULL) 351385732ac8SCy Schubert goto error; 35145b9c547cSRui Paulo 35155b9c547cSRui Paulo if (policy == DO_NOT_SECURE) { 35165b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp); 35175b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp); 35185b9c547cSRui Paulo } else { 35195b9c547cSRui Paulo kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE, 35205b9c547cSRui Paulo kay_l2_receive, kay, 1); 35215b9c547cSRui Paulo if (kay->l2_mka == NULL) { 35225b9c547cSRui Paulo wpa_printf(MSG_WARNING, 35235b9c547cSRui Paulo "KaY: Failed to initialize L2 packet processing for MKA packet"); 352485732ac8SCy Schubert goto error; 35255b9c547cSRui Paulo } 35265b9c547cSRui Paulo } 35275b9c547cSRui Paulo 35285b9c547cSRui Paulo return kay; 352985732ac8SCy Schubert 353085732ac8SCy Schubert error: 353185732ac8SCy Schubert ieee802_1x_kay_deinit(kay); 353285732ac8SCy Schubert return NULL; 35335b9c547cSRui Paulo } 35345b9c547cSRui Paulo 35355b9c547cSRui Paulo 35365b9c547cSRui Paulo /** 35375b9c547cSRui Paulo * ieee802_1x_kay_deinit - 35385b9c547cSRui Paulo */ 35395b9c547cSRui Paulo void 35405b9c547cSRui Paulo ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) 35415b9c547cSRui Paulo { 35425b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 35435b9c547cSRui Paulo 35445b9c547cSRui Paulo if (!kay) 35455b9c547cSRui Paulo return; 35465b9c547cSRui Paulo 35475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine removed"); 35485b9c547cSRui Paulo 35495b9c547cSRui Paulo while (!dl_list_empty(&kay->participant_list)) { 35505b9c547cSRui Paulo participant = dl_list_entry(kay->participant_list.next, 35515b9c547cSRui Paulo struct ieee802_1x_mka_participant, 35525b9c547cSRui Paulo list); 35535b9c547cSRui Paulo ieee802_1x_kay_delete_mka(kay, &participant->ckn); 35545b9c547cSRui Paulo } 35555b9c547cSRui Paulo 35565b9c547cSRui Paulo ieee802_1x_cp_sm_deinit(kay->cp); 35575b9c547cSRui Paulo secy_deinit_macsec(kay); 35585b9c547cSRui Paulo 35595b9c547cSRui Paulo if (kay->l2_mka) { 35605b9c547cSRui Paulo l2_packet_deinit(kay->l2_mka); 35615b9c547cSRui Paulo kay->l2_mka = NULL; 35625b9c547cSRui Paulo } 35635b9c547cSRui Paulo 35645b9c547cSRui Paulo os_free(kay->ctx); 35655b9c547cSRui Paulo os_free(kay); 35665b9c547cSRui Paulo } 35675b9c547cSRui Paulo 35685b9c547cSRui Paulo 35694bc52338SCy Schubert static const char * mode_txt(enum mka_created_mode mode) 35704bc52338SCy Schubert { 35714bc52338SCy Schubert switch (mode) { 35724bc52338SCy Schubert case PSK: 35734bc52338SCy Schubert return "PSK"; 35744bc52338SCy Schubert case EAP_EXCHANGE: 35754bc52338SCy Schubert return "EAP"; 35764bc52338SCy Schubert } 35774bc52338SCy Schubert 35784bc52338SCy Schubert return "?"; 35794bc52338SCy Schubert } 35804bc52338SCy Schubert 35814bc52338SCy Schubert 35825b9c547cSRui Paulo /** 35835b9c547cSRui Paulo * ieee802_1x_kay_create_mka - 35845b9c547cSRui Paulo */ 35855b9c547cSRui Paulo struct ieee802_1x_mka_participant * 358685732ac8SCy Schubert ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, 358785732ac8SCy Schubert const struct mka_key_name *ckn, 358885732ac8SCy Schubert const struct mka_key *cak, u32 life, 35895b9c547cSRui Paulo enum mka_created_mode mode, Boolean is_authenticator) 35905b9c547cSRui Paulo { 35915b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 35925b9c547cSRui Paulo unsigned int usecs; 35935b9c547cSRui Paulo 35944bc52338SCy Schubert wpa_printf(MSG_DEBUG, 35954bc52338SCy Schubert "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)", 35964bc52338SCy Schubert kay->if_name, mode_txt(mode), yes_no(is_authenticator)); 35974bc52338SCy Schubert 35985b9c547cSRui Paulo if (!kay || !ckn || !cak) { 35995b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: ckn or cak is null"); 36005b9c547cSRui Paulo return NULL; 36015b9c547cSRui Paulo } 36025b9c547cSRui Paulo 36034bc52338SCy Schubert if (cak->len != 16 && cak->len != 32) { 36044bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u", 36054bc52338SCy Schubert (unsigned int) cak->len); 36065b9c547cSRui Paulo return NULL; 36075b9c547cSRui Paulo } 36085b9c547cSRui Paulo if (ckn->len > MAX_CKN_LEN) { 36094bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)"); 36105b9c547cSRui Paulo return NULL; 36115b9c547cSRui Paulo } 36125b9c547cSRui Paulo if (!kay->enable) { 36135b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: Now is at disable state"); 36145b9c547cSRui Paulo return NULL; 36155b9c547cSRui Paulo } 36165b9c547cSRui Paulo 36175b9c547cSRui Paulo participant = os_zalloc(sizeof(*participant)); 36185b9c547cSRui Paulo if (!participant) { 36195b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); 36205b9c547cSRui Paulo return NULL; 36215b9c547cSRui Paulo } 36225b9c547cSRui Paulo 36235b9c547cSRui Paulo participant->ckn.len = ckn->len; 36245b9c547cSRui Paulo os_memcpy(participant->ckn.name, ckn->name, ckn->len); 36254bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name, 36264bc52338SCy Schubert participant->ckn.len); 36275b9c547cSRui Paulo participant->cak.len = cak->len; 36285b9c547cSRui Paulo os_memcpy(participant->cak.key, cak->key, cak->len); 36294bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key, 36304bc52338SCy Schubert participant->cak.len); 36315b9c547cSRui Paulo if (life) 36325b9c547cSRui Paulo participant->cak_life = life + time(NULL); 36335b9c547cSRui Paulo 36345b9c547cSRui Paulo switch (mode) { 36355b9c547cSRui Paulo case EAP_EXCHANGE: 36365b9c547cSRui Paulo if (is_authenticator) { 36375b9c547cSRui Paulo participant->is_obliged_key_server = TRUE; 36385b9c547cSRui Paulo participant->can_be_key_server = TRUE; 36395b9c547cSRui Paulo participant->is_key_server = TRUE; 36405b9c547cSRui Paulo participant->principal = TRUE; 36415b9c547cSRui Paulo 36425b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci, 36435b9c547cSRui Paulo sizeof(kay->key_server_sci)); 36445b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority; 36455b9c547cSRui Paulo participant->is_elected = TRUE; 36465b9c547cSRui Paulo } else { 36475b9c547cSRui Paulo participant->is_obliged_key_server = FALSE; 36485b9c547cSRui Paulo participant->can_be_key_server = FALSE; 36495b9c547cSRui Paulo participant->is_key_server = FALSE; 36505b9c547cSRui Paulo participant->is_elected = TRUE; 36515b9c547cSRui Paulo } 36525b9c547cSRui Paulo break; 36535b9c547cSRui Paulo 36545b9c547cSRui Paulo default: 36555b9c547cSRui Paulo participant->is_obliged_key_server = FALSE; 36565b9c547cSRui Paulo participant->can_be_key_server = TRUE; 3657780fb4a2SCy Schubert participant->is_key_server = TRUE; 36585b9c547cSRui Paulo participant->is_elected = FALSE; 36595b9c547cSRui Paulo break; 36605b9c547cSRui Paulo } 36615b9c547cSRui Paulo 36625b9c547cSRui Paulo participant->cached = FALSE; 36635b9c547cSRui Paulo 36645b9c547cSRui Paulo participant->active = FALSE; 36655b9c547cSRui Paulo participant->participant = FALSE; 36665b9c547cSRui Paulo participant->retain = FALSE; 36675b9c547cSRui Paulo participant->activate = DEFAULT; 36685b9c547cSRui Paulo 36695b9c547cSRui Paulo if (participant->is_key_server) 36705b9c547cSRui Paulo participant->principal = TRUE; 36715b9c547cSRui Paulo 36725b9c547cSRui Paulo dl_list_init(&participant->live_peers); 36735b9c547cSRui Paulo dl_list_init(&participant->potential_peers); 36745b9c547cSRui Paulo 36755b9c547cSRui Paulo participant->retry_count = 0; 36765b9c547cSRui Paulo participant->kay = kay; 36775b9c547cSRui Paulo 3678780fb4a2SCy Schubert if (!reset_participant_mi(participant)) 36795b9c547cSRui Paulo goto fail; 36804bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s", 36814bc52338SCy Schubert mi_txt(participant->mi)); 36825b9c547cSRui Paulo 36835b9c547cSRui Paulo participant->lrx = FALSE; 36845b9c547cSRui Paulo participant->ltx = FALSE; 36855b9c547cSRui Paulo participant->orx = FALSE; 36865b9c547cSRui Paulo participant->otx = FALSE; 36875b9c547cSRui Paulo participant->to_dist_sak = FALSE; 36885b9c547cSRui Paulo participant->to_use_sak = FALSE; 36895b9c547cSRui Paulo participant->new_sak = FALSE; 36905b9c547cSRui Paulo dl_list_init(&participant->sak_list); 36915b9c547cSRui Paulo participant->new_key = NULL; 36925b9c547cSRui Paulo dl_list_init(&participant->rxsc_list); 369385732ac8SCy Schubert participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci); 36945b9c547cSRui Paulo secy_cp_control_protect_frames(kay, kay->macsec_protect); 36955b9c547cSRui Paulo secy_cp_control_replay(kay, kay->macsec_replay_protect, 36965b9c547cSRui Paulo kay->macsec_replay_window); 36974bc52338SCy Schubert if (secy_create_transmit_sc(kay, participant->txsc)) 36984bc52338SCy Schubert goto fail; 36995b9c547cSRui Paulo 37005b9c547cSRui Paulo /* to derive KEK from CAK and CKN */ 37014bc52338SCy Schubert participant->kek.len = participant->cak.len; 37025b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key, 37034bc52338SCy Schubert participant->cak.len, 37045b9c547cSRui Paulo participant->ckn.name, 37055b9c547cSRui Paulo participant->ckn.len, 37064bc52338SCy Schubert participant->kek.key, 37074bc52338SCy Schubert participant->kek.len)) { 37084bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: KEK derivation failed"); 37095b9c547cSRui Paulo goto fail; 37105b9c547cSRui Paulo } 37115b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK", 37125b9c547cSRui Paulo participant->kek.key, participant->kek.len); 37135b9c547cSRui Paulo 37145b9c547cSRui Paulo /* to derive ICK from CAK and CKN */ 37154bc52338SCy Schubert participant->ick.len = participant->cak.len; 37165b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key, 37174bc52338SCy Schubert participant->cak.len, 37185b9c547cSRui Paulo participant->ckn.name, 37195b9c547cSRui Paulo participant->ckn.len, 37204bc52338SCy Schubert participant->ick.key, 37214bc52338SCy Schubert participant->ick.len)) { 37224bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: ICK derivation failed"); 37235b9c547cSRui Paulo goto fail; 37245b9c547cSRui Paulo } 37255b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK", 37265b9c547cSRui Paulo participant->ick.key, participant->ick.len); 37275b9c547cSRui Paulo 37285b9c547cSRui Paulo dl_list_add(&kay->participant_list, &participant->list); 37295b9c547cSRui Paulo 37304bc52338SCy Schubert usecs = os_random() % (kay->mka_hello_time * 1000); 37315b9c547cSRui Paulo eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, 37325b9c547cSRui Paulo participant, NULL); 373385732ac8SCy Schubert 373485732ac8SCy Schubert /* Disable MKA lifetime for PSK mode. 373585732ac8SCy Schubert * The peer(s) can take a long time to come up, because we 373685732ac8SCy Schubert * create a "standby" MKA, and we need it to remain live until 373785732ac8SCy Schubert * some peer appears. 373885732ac8SCy Schubert */ 373985732ac8SCy Schubert if (mode != PSK) { 37405b9c547cSRui Paulo participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + 37415b9c547cSRui Paulo usecs / 1000000; 374285732ac8SCy Schubert } 374385732ac8SCy Schubert participant->mode = mode; 37445b9c547cSRui Paulo 37455b9c547cSRui Paulo return participant; 37465b9c547cSRui Paulo 37475b9c547cSRui Paulo fail: 37484bc52338SCy Schubert os_free(participant->txsc); 37495b9c547cSRui Paulo os_free(participant); 37505b9c547cSRui Paulo return NULL; 37515b9c547cSRui Paulo } 37525b9c547cSRui Paulo 37535b9c547cSRui Paulo 37545b9c547cSRui Paulo /** 37555b9c547cSRui Paulo * ieee802_1x_kay_delete_mka - 37565b9c547cSRui Paulo */ 37575b9c547cSRui Paulo void 37585b9c547cSRui Paulo ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) 37595b9c547cSRui Paulo { 37605b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 37615b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer; 37625b9c547cSRui Paulo struct data_key *sak; 37635b9c547cSRui Paulo struct receive_sc *rxsc; 37645b9c547cSRui Paulo 37655b9c547cSRui Paulo if (!kay || !ckn) 37665b9c547cSRui Paulo return; 37675b9c547cSRui Paulo 37685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: participant removed"); 37695b9c547cSRui Paulo 37705b9c547cSRui Paulo /* get the participant */ 377185732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); 37725b9c547cSRui Paulo if (!participant) { 37735b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "KaY: participant is not found", 37745b9c547cSRui Paulo ckn->name, ckn->len); 37755b9c547cSRui Paulo return; 37765b9c547cSRui Paulo } 37775b9c547cSRui Paulo 3778780fb4a2SCy Schubert eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL); 37795b9c547cSRui Paulo dl_list_del(&participant->list); 37805b9c547cSRui Paulo 37815b9c547cSRui Paulo /* remove live peer */ 37825b9c547cSRui Paulo while (!dl_list_empty(&participant->live_peers)) { 37835b9c547cSRui Paulo peer = dl_list_entry(participant->live_peers.next, 37845b9c547cSRui Paulo struct ieee802_1x_kay_peer, list); 37855b9c547cSRui Paulo dl_list_del(&peer->list); 37865b9c547cSRui Paulo os_free(peer); 37875b9c547cSRui Paulo } 37885b9c547cSRui Paulo 37895b9c547cSRui Paulo /* remove potential peer */ 37905b9c547cSRui Paulo while (!dl_list_empty(&participant->potential_peers)) { 37915b9c547cSRui Paulo peer = dl_list_entry(participant->potential_peers.next, 37925b9c547cSRui Paulo struct ieee802_1x_kay_peer, list); 37935b9c547cSRui Paulo dl_list_del(&peer->list); 37945b9c547cSRui Paulo os_free(peer); 37955b9c547cSRui Paulo } 37965b9c547cSRui Paulo 37975b9c547cSRui Paulo /* remove sak */ 37985b9c547cSRui Paulo while (!dl_list_empty(&participant->sak_list)) { 37995b9c547cSRui Paulo sak = dl_list_entry(participant->sak_list.next, 38005b9c547cSRui Paulo struct data_key, list); 38015b9c547cSRui Paulo dl_list_del(&sak->list); 380285732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(sak); 38035b9c547cSRui Paulo } 38045b9c547cSRui Paulo while (!dl_list_empty(&participant->rxsc_list)) { 38055b9c547cSRui Paulo rxsc = dl_list_entry(participant->rxsc_list.next, 38065b9c547cSRui Paulo struct receive_sc, list); 38075b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(participant, rxsc); 38085b9c547cSRui Paulo } 38095b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); 38105b9c547cSRui Paulo 38115b9c547cSRui Paulo os_memset(&participant->cak, 0, sizeof(participant->cak)); 38125b9c547cSRui Paulo os_memset(&participant->kek, 0, sizeof(participant->kek)); 38135b9c547cSRui Paulo os_memset(&participant->ick, 0, sizeof(participant->ick)); 38145b9c547cSRui Paulo os_free(participant); 38155b9c547cSRui Paulo } 38165b9c547cSRui Paulo 38175b9c547cSRui Paulo 38185b9c547cSRui Paulo /** 38195b9c547cSRui Paulo * ieee802_1x_kay_mka_participate - 38205b9c547cSRui Paulo */ 38215b9c547cSRui Paulo void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, 38225b9c547cSRui Paulo struct mka_key_name *ckn, 38235b9c547cSRui Paulo Boolean status) 38245b9c547cSRui Paulo { 38255b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 38265b9c547cSRui Paulo 38275b9c547cSRui Paulo if (!kay || !ckn) 38285b9c547cSRui Paulo return; 38295b9c547cSRui Paulo 383085732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); 38315b9c547cSRui Paulo if (!participant) 38325b9c547cSRui Paulo return; 38335b9c547cSRui Paulo 38345b9c547cSRui Paulo participant->active = status; 38355b9c547cSRui Paulo } 38365b9c547cSRui Paulo 38375b9c547cSRui Paulo 38385b9c547cSRui Paulo /** 38395b9c547cSRui Paulo * ieee802_1x_kay_new_sak - 38405b9c547cSRui Paulo */ 38415b9c547cSRui Paulo int 38425b9c547cSRui Paulo ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay) 38435b9c547cSRui Paulo { 38445b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 38455b9c547cSRui Paulo 38465b9c547cSRui Paulo if (!kay) 38475b9c547cSRui Paulo return -1; 38485b9c547cSRui Paulo 38495b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay); 38505b9c547cSRui Paulo if (!participant) 38515b9c547cSRui Paulo return -1; 38525b9c547cSRui Paulo 38535b9c547cSRui Paulo participant->new_sak = TRUE; 38545b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: new SAK signal"); 38555b9c547cSRui Paulo 38565b9c547cSRui Paulo return 0; 38575b9c547cSRui Paulo } 38585b9c547cSRui Paulo 38595b9c547cSRui Paulo 38605b9c547cSRui Paulo /** 38615b9c547cSRui Paulo * ieee802_1x_kay_change_cipher_suite - 38625b9c547cSRui Paulo */ 38635b9c547cSRui Paulo int 3864780fb4a2SCy Schubert ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, 3865780fb4a2SCy Schubert unsigned int cs_index) 38665b9c547cSRui Paulo { 38675b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant; 386885732ac8SCy Schubert enum macsec_cap secy_cap; 38695b9c547cSRui Paulo 38705b9c547cSRui Paulo if (!kay) 38715b9c547cSRui Paulo return -1; 38725b9c547cSRui Paulo 3873780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE) { 38745b9c547cSRui Paulo wpa_printf(MSG_ERROR, 38755b9c547cSRui Paulo "KaY: Configured cipher suite index is out of range"); 38765b9c547cSRui Paulo return -1; 38775b9c547cSRui Paulo } 38785b9c547cSRui Paulo if (kay->macsec_csindex == cs_index) 38795b9c547cSRui Paulo return -2; 38805b9c547cSRui Paulo 38815b9c547cSRui Paulo if (cs_index == 0) 38825b9c547cSRui Paulo kay->macsec_desired = FALSE; 38835b9c547cSRui Paulo 38845b9c547cSRui Paulo kay->macsec_csindex = cs_index; 38855b9c547cSRui Paulo kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable; 38865b9c547cSRui Paulo 388785732ac8SCy Schubert if (secy_get_capability(kay, &secy_cap) < 0) 388885732ac8SCy Schubert return -3; 388985732ac8SCy Schubert 389085732ac8SCy Schubert if (kay->macsec_capable > secy_cap) 389185732ac8SCy Schubert kay->macsec_capable = secy_cap; 389285732ac8SCy Schubert 38935b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay); 38945b9c547cSRui Paulo if (participant) { 38955b9c547cSRui Paulo wpa_printf(MSG_INFO, "KaY: Cipher Suite changed"); 38965b9c547cSRui Paulo participant->new_sak = TRUE; 38975b9c547cSRui Paulo } 38985b9c547cSRui Paulo 38995b9c547cSRui Paulo return 0; 39005b9c547cSRui Paulo } 390185732ac8SCy Schubert 390285732ac8SCy Schubert 390385732ac8SCy Schubert #ifdef CONFIG_CTRL_IFACE 39044bc52338SCy Schubert 390585732ac8SCy Schubert /** 390685732ac8SCy Schubert * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details 390785732ac8SCy Schubert * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() 390885732ac8SCy Schubert * @buf: Buffer for status information 390985732ac8SCy Schubert * @buflen: Maximum buffer length 391085732ac8SCy Schubert * @verbose: Whether to include verbose status information 391185732ac8SCy Schubert * Returns: Number of bytes written to buf. 391285732ac8SCy Schubert * 39134bc52338SCy Schubert * Query KaY status information. This function fills in a text area with current 391485732ac8SCy Schubert * status information. If the buffer (buf) is not large enough, status 391585732ac8SCy Schubert * information will be truncated to fit the buffer. 391685732ac8SCy Schubert */ 391785732ac8SCy Schubert int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, 391885732ac8SCy Schubert size_t buflen) 391985732ac8SCy Schubert { 39204bc52338SCy Schubert char *pos, *end; 39214bc52338SCy Schubert int res, count; 39224bc52338SCy Schubert struct ieee802_1x_mka_participant *p; 392385732ac8SCy Schubert 392485732ac8SCy Schubert if (!kay) 392585732ac8SCy Schubert return 0; 392685732ac8SCy Schubert 39274bc52338SCy Schubert pos = buf; 39284bc52338SCy Schubert end = buf + buflen; 39294bc52338SCy Schubert 39304bc52338SCy Schubert res = os_snprintf(pos, end - pos, 393185732ac8SCy Schubert "PAE KaY status=%s\n" 393285732ac8SCy Schubert "Authenticated=%s\n" 393385732ac8SCy Schubert "Secured=%s\n" 393485732ac8SCy Schubert "Failed=%s\n" 393585732ac8SCy Schubert "Actor Priority=%u\n" 393685732ac8SCy Schubert "Key Server Priority=%u\n" 393785732ac8SCy Schubert "Is Key Server=%s\n" 393885732ac8SCy Schubert "Number of Keys Distributed=%u\n" 39394bc52338SCy Schubert "Number of Keys Received=%u\n" 39404bc52338SCy Schubert "MKA Hello Time=%u\n", 394185732ac8SCy Schubert kay->active ? "Active" : "Not-Active", 394285732ac8SCy Schubert kay->authenticated ? "Yes" : "No", 394385732ac8SCy Schubert kay->secured ? "Yes" : "No", 394485732ac8SCy Schubert kay->failed ? "Yes" : "No", 394585732ac8SCy Schubert kay->actor_priority, 394685732ac8SCy Schubert kay->key_server_priority, 394785732ac8SCy Schubert kay->is_key_server ? "Yes" : "No", 394885732ac8SCy Schubert kay->dist_kn - 1, 39494bc52338SCy Schubert kay->rcvd_keys, 39504bc52338SCy Schubert kay->mka_hello_time); 39514bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 39524bc52338SCy Schubert return 0; 39534bc52338SCy Schubert pos += res; 39544bc52338SCy Schubert 39554bc52338SCy Schubert res = os_snprintf(pos, end - pos, 39564bc52338SCy Schubert "actor_sci=%s\n", sci_txt(&kay->actor_sci)); 39574bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 39584bc52338SCy Schubert return end - pos; 39594bc52338SCy Schubert pos += res; 39604bc52338SCy Schubert 39614bc52338SCy Schubert res = os_snprintf(pos, end - pos, 39624bc52338SCy Schubert "key_server_sci=%s\n", sci_txt(&kay->key_server_sci)); 39634bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 39644bc52338SCy Schubert return end - pos; 39654bc52338SCy Schubert pos += res; 39664bc52338SCy Schubert 39674bc52338SCy Schubert count = 0; 39684bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list, 39694bc52338SCy Schubert struct ieee802_1x_mka_participant, list) { 39704bc52338SCy Schubert char *pos2 = pos; 39714bc52338SCy Schubert 39724bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=", 39734bc52338SCy Schubert count); 39744bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 39754bc52338SCy Schubert return end - pos; 39764bc52338SCy Schubert pos2 += res; 39774bc52338SCy Schubert count++; 39784bc52338SCy Schubert 39794bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, 39804bc52338SCy Schubert p->ckn.len); 39814bc52338SCy Schubert 39824bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, 39834bc52338SCy Schubert "\nmi=%s\n" 39844bc52338SCy Schubert "mn=%u\n" 39854bc52338SCy Schubert "active=%s\n" 39864bc52338SCy Schubert "participant=%s\n" 39874bc52338SCy Schubert "retain=%s\n" 39884bc52338SCy Schubert "live_peers=%u\n" 39894bc52338SCy Schubert "potential_peers=%u\n" 39904bc52338SCy Schubert "is_key_server=%s\n" 39914bc52338SCy Schubert "is_elected=%s\n", 39924bc52338SCy Schubert mi_txt(p->mi), p->mn, 39934bc52338SCy Schubert yes_no(p->active), 39944bc52338SCy Schubert yes_no(p->participant), 39954bc52338SCy Schubert yes_no(p->retain), 39964bc52338SCy Schubert dl_list_len(&p->live_peers), 39974bc52338SCy Schubert dl_list_len(&p->potential_peers), 39984bc52338SCy Schubert yes_no(p->is_key_server), 39994bc52338SCy Schubert yes_no(p->is_elected)); 40004bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 40014bc52338SCy Schubert return end - pos; 40024bc52338SCy Schubert pos2 += res; 40034bc52338SCy Schubert pos = pos2; 40044bc52338SCy Schubert } 40054bc52338SCy Schubert 40064bc52338SCy Schubert return pos - buf; 40074bc52338SCy Schubert } 40084bc52338SCy Schubert 40094bc52338SCy Schubert 40104bc52338SCy Schubert static const char * true_false(Boolean val) 40114bc52338SCy Schubert { 40124bc52338SCy Schubert return val ? "true" : "false"; 40134bc52338SCy Schubert } 40144bc52338SCy Schubert 40154bc52338SCy Schubert 40164bc52338SCy Schubert static const char * activate_control_txt(enum activate_ctrl activate) 40174bc52338SCy Schubert { 40184bc52338SCy Schubert switch (activate) { 40194bc52338SCy Schubert case DEFAULT: 40204bc52338SCy Schubert return "default"; 40214bc52338SCy Schubert case DISABLED: 40224bc52338SCy Schubert return "disabled"; 40234bc52338SCy Schubert case ON_OPER_UP: 40244bc52338SCy Schubert return "onOperUp"; 40254bc52338SCy Schubert case ALWAYS: 40264bc52338SCy Schubert return "always"; 40274bc52338SCy Schubert } 40284bc52338SCy Schubert 40294bc52338SCy Schubert return "?"; 40304bc52338SCy Schubert } 40314bc52338SCy Schubert 40324bc52338SCy Schubert 40334bc52338SCy Schubert static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf, 40344bc52338SCy Schubert char *end) 40354bc52338SCy Schubert { 40364bc52338SCy Schubert char *pos = buf; 40374bc52338SCy Schubert struct ieee802_1x_kay_peer *p; 40384bc52338SCy Schubert int res; 40394bc52338SCy Schubert 40404bc52338SCy Schubert dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) { 40414bc52338SCy Schubert res = os_snprintf(pos, end - pos, 40424bc52338SCy Schubert "ieee8021XKayMkaPeerListMI=%s\n" 40434bc52338SCy Schubert "ieee8021XKayMkaPeerListMN=%u\n" 40444bc52338SCy Schubert "ieee8021XKayMkaPeerListType=%u\n" 40454bc52338SCy Schubert "ieee8021XKayMkaPeerListSCI=%s\n", 40464bc52338SCy Schubert mi_txt(p->mi), 40474bc52338SCy Schubert p->mn, 40484bc52338SCy Schubert live ? 1 : 2, 40494bc52338SCy Schubert sci_txt(&p->sci)); 40504bc52338SCy Schubert if (os_snprintf_error(end - pos, res)) 40514bc52338SCy Schubert return pos; 40524bc52338SCy Schubert pos += res; 40534bc52338SCy Schubert } 40544bc52338SCy Schubert 40554bc52338SCy Schubert return pos; 40564bc52338SCy Schubert } 40574bc52338SCy Schubert 40584bc52338SCy Schubert 40594bc52338SCy Schubert int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, 40604bc52338SCy Schubert size_t buflen) 40614bc52338SCy Schubert { 40624bc52338SCy Schubert char *pos, *end; 40634bc52338SCy Schubert int res; 40644bc52338SCy Schubert struct ieee802_1x_mka_participant *p; 40654bc52338SCy Schubert 40664bc52338SCy Schubert if (!kay) 406785732ac8SCy Schubert return 0; 406885732ac8SCy Schubert 40694bc52338SCy Schubert pos = buf; 40704bc52338SCy Schubert end = buf + buflen; 40714bc52338SCy Schubert 40724bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list, 40734bc52338SCy Schubert struct ieee802_1x_mka_participant, list) { 40744bc52338SCy Schubert char *pos2 = pos; 40754bc52338SCy Schubert 40764bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN="); 40774bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 40784bc52338SCy Schubert return end - pos; 40794bc52338SCy Schubert pos2 += res; 40804bc52338SCy Schubert 40814bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, 40824bc52338SCy Schubert p->ckn.len); 40834bc52338SCy Schubert 40844bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, 40854bc52338SCy Schubert "\nieee8021XKayMkaPartCached=%s\n" 40864bc52338SCy Schubert "ieee8021XKayMkaPartActive=%s\n" 40874bc52338SCy Schubert "ieee8021XKayMkaPartRetain=%s\n" 40884bc52338SCy Schubert "ieee8021XKayMkaPartActivateControl=%s\n" 40894bc52338SCy Schubert "ieee8021XKayMkaPartPrincipal=%s\n", 40904bc52338SCy Schubert true_false(p->cached), 40914bc52338SCy Schubert true_false(p->active), 40924bc52338SCy Schubert true_false(p->retain), 40934bc52338SCy Schubert activate_control_txt(p->activate), 40944bc52338SCy Schubert true_false(p->principal)); 40954bc52338SCy Schubert if (os_snprintf_error(buflen, res)) 40964bc52338SCy Schubert return end - pos; 40974bc52338SCy Schubert pos2 += res; 40984bc52338SCy Schubert pos = pos2; 40994bc52338SCy Schubert 41004bc52338SCy Schubert pos = mka_mib_peer(&p->live_peers, TRUE, pos, end); 41014bc52338SCy Schubert pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end); 410285732ac8SCy Schubert } 41034bc52338SCy Schubert 41044bc52338SCy Schubert return pos - buf; 41054bc52338SCy Schubert } 41064bc52338SCy Schubert 410785732ac8SCy Schubert #endif /* CONFIG_CTRL_IFACE */ 4108