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
is_ki_equal(struct ieee802_1x_mka_ki * ki1,struct ieee802_1x_mka_ki * ki2)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
set_mka_param_body_len(void * body,unsigned int len)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
get_mka_param_body_len(const void * body)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
get_mka_param_body_type(const void * body)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
mi_txt(const u8 * mi)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
sci_txt(const struct ieee802_1x_mka_sci * sci)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
algo_agility_txt(const u8 * algo_agility)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
ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body * body)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
ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body * body)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
ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body * body)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));
224*a90b9d01SCy Schubert if (body_len == 28) {
225*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:",
226*a90b9d01SCy Schubert body->sak, 24);
227*a90b9d01SCy Schubert } else if (body_len > CS_ID_LEN - sizeof(body->kn)) {
228*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "\tMACsec Cipher Suite...:",
229*a90b9d01SCy Schubert body->sak, CS_ID_LEN);
230*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:",
231*a90b9d01SCy Schubert body->sak + CS_ID_LEN,
232*a90b9d01SCy Schubert body_len - CS_ID_LEN - sizeof(body->kn));
233*a90b9d01SCy Schubert }
2345b9c547cSRui Paulo }
2355b9c547cSRui Paulo
2365b9c547cSRui Paulo
yes_no(int val)2375b9c547cSRui Paulo static const char * yes_no(int val)
2385b9c547cSRui Paulo {
2395b9c547cSRui Paulo return val ? "Yes" : "No";
2405b9c547cSRui Paulo }
2415b9c547cSRui Paulo
2425b9c547cSRui Paulo
2435b9c547cSRui Paulo /**
2445b9c547cSRui Paulo * ieee802_1x_mka_dump_sak_use_body -
2455b9c547cSRui Paulo */
2465b9c547cSRui Paulo static void
ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body * body)2475b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
2485b9c547cSRui Paulo {
2495b9c547cSRui Paulo int body_len;
2505b9c547cSRui Paulo
2515b9c547cSRui Paulo if (body == NULL)
2525b9c547cSRui Paulo return;
2535b9c547cSRui Paulo
2544bc52338SCy Schubert /* IEEE Std 802.1X-2010, Figure 11-10 */
2555b9c547cSRui Paulo body_len = get_mka_param_body_len(body);
2564bc52338SCy Schubert wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set");
2575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
2585b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
2595b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
2604bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan);
2614bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx));
2624bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx));
2634bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx));
2644bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx));
2655b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
2665b9c547cSRui Paulo yes_no(body->delay_protect));
2675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
2685b9c547cSRui Paulo if (!body_len)
2695b9c547cSRui Paulo return;
2705b9c547cSRui Paulo
2714bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi));
2725b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
2735b9c547cSRui Paulo be_to_host32(body->lkn));
2745b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
2755b9c547cSRui Paulo be_to_host32(body->llpn));
2764bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi));
2774bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u",
2785b9c547cSRui Paulo be_to_host32(body->okn));
2794bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u",
2805b9c547cSRui Paulo be_to_host32(body->olpn));
2815b9c547cSRui Paulo }
2825b9c547cSRui Paulo
2835b9c547cSRui Paulo
2845b9c547cSRui Paulo /**
2855b9c547cSRui Paulo * ieee802_1x_kay_get_participant -
2865b9c547cSRui Paulo */
2875b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_participant(struct ieee802_1x_kay * kay,const u8 * ckn,size_t len)28885732ac8SCy Schubert ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn,
28985732ac8SCy Schubert size_t len)
2905b9c547cSRui Paulo {
2915b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
2925b9c547cSRui Paulo
2935b9c547cSRui Paulo dl_list_for_each(participant, &kay->participant_list,
2945b9c547cSRui Paulo struct ieee802_1x_mka_participant, list) {
29585732ac8SCy Schubert if (participant->ckn.len == len &&
29685732ac8SCy Schubert os_memcmp(participant->ckn.name, ckn,
2975b9c547cSRui Paulo participant->ckn.len) == 0)
2985b9c547cSRui Paulo return participant;
2995b9c547cSRui Paulo }
3005b9c547cSRui Paulo
3015b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: participant is not found");
3025b9c547cSRui Paulo
3035b9c547cSRui Paulo return NULL;
3045b9c547cSRui Paulo }
3055b9c547cSRui Paulo
3065b9c547cSRui Paulo
3075b9c547cSRui Paulo /**
3085b9c547cSRui Paulo * ieee802_1x_kay_get_principal_participant -
3095b9c547cSRui Paulo */
3105b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay * kay)3115b9c547cSRui Paulo ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
3125b9c547cSRui Paulo {
3135b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
3145b9c547cSRui Paulo
3155b9c547cSRui Paulo dl_list_for_each(participant, &kay->participant_list,
3165b9c547cSRui Paulo struct ieee802_1x_mka_participant, list) {
3175b9c547cSRui Paulo if (participant->principal)
3185b9c547cSRui Paulo return participant;
3195b9c547cSRui Paulo }
3205b9c547cSRui Paulo
321780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: principal participant is not found");
3225b9c547cSRui Paulo return NULL;
3235b9c547cSRui Paulo }
3245b9c547cSRui Paulo
3255b9c547cSRui Paulo
get_peer_mi(struct dl_list * peers,const u8 * mi)3265b9c547cSRui Paulo static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
3275b9c547cSRui Paulo const u8 *mi)
3285b9c547cSRui Paulo {
3295b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
3305b9c547cSRui Paulo
3315b9c547cSRui Paulo dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
3325b9c547cSRui Paulo if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
3335b9c547cSRui Paulo return peer;
3345b9c547cSRui Paulo }
3355b9c547cSRui Paulo
3365b9c547cSRui Paulo return NULL;
3375b9c547cSRui Paulo }
3385b9c547cSRui Paulo
3395b9c547cSRui Paulo
3405b9c547cSRui Paulo /**
341780fb4a2SCy Schubert * ieee802_1x_kay_get_potential_peer
3425b9c547cSRui Paulo */
3435b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_potential_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi)344780fb4a2SCy Schubert ieee802_1x_kay_get_potential_peer(
345780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi)
3465b9c547cSRui Paulo {
3475b9c547cSRui Paulo return get_peer_mi(&participant->potential_peers, mi);
3485b9c547cSRui Paulo }
3495b9c547cSRui Paulo
3505b9c547cSRui Paulo
3515b9c547cSRui Paulo /**
3525b9c547cSRui Paulo * ieee802_1x_kay_get_live_peer
3535b9c547cSRui Paulo */
3545b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi)3555b9c547cSRui Paulo ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
3565b9c547cSRui Paulo const u8 *mi)
3575b9c547cSRui Paulo {
3585b9c547cSRui Paulo return get_peer_mi(&participant->live_peers, mi);
3595b9c547cSRui Paulo }
3605b9c547cSRui Paulo
3615b9c547cSRui Paulo
3625b9c547cSRui Paulo /**
363780fb4a2SCy Schubert * ieee802_1x_kay_is_in_potential_peer
364780fb4a2SCy Schubert */
365c1d255d3SCy Schubert static bool
ieee802_1x_kay_is_in_potential_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi)366780fb4a2SCy Schubert ieee802_1x_kay_is_in_potential_peer(
367780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi)
368780fb4a2SCy Schubert {
369780fb4a2SCy Schubert return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL;
370780fb4a2SCy Schubert }
371780fb4a2SCy Schubert
372780fb4a2SCy Schubert
373780fb4a2SCy Schubert /**
374780fb4a2SCy Schubert * ieee802_1x_kay_is_in_live_peer
375780fb4a2SCy Schubert */
376c1d255d3SCy Schubert static bool
ieee802_1x_kay_is_in_live_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi)377780fb4a2SCy Schubert ieee802_1x_kay_is_in_live_peer(
378780fb4a2SCy Schubert struct ieee802_1x_mka_participant *participant, const u8 *mi)
379780fb4a2SCy Schubert {
380780fb4a2SCy Schubert return ieee802_1x_kay_get_live_peer(participant, mi) != NULL;
381780fb4a2SCy Schubert }
382780fb4a2SCy Schubert
383780fb4a2SCy Schubert
384780fb4a2SCy Schubert /**
385780fb4a2SCy Schubert * ieee802_1x_kay_get_peer
386780fb4a2SCy Schubert */
387780fb4a2SCy Schubert static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi)388780fb4a2SCy Schubert ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
389780fb4a2SCy Schubert const u8 *mi)
390780fb4a2SCy Schubert {
391780fb4a2SCy Schubert struct ieee802_1x_kay_peer *peer;
392780fb4a2SCy Schubert
393780fb4a2SCy Schubert peer = ieee802_1x_kay_get_live_peer(participant, mi);
394780fb4a2SCy Schubert if (peer)
395780fb4a2SCy Schubert return peer;
396780fb4a2SCy Schubert
397780fb4a2SCy Schubert return ieee802_1x_kay_get_potential_peer(participant, mi);
398780fb4a2SCy Schubert }
399780fb4a2SCy Schubert
400780fb4a2SCy Schubert
401780fb4a2SCy Schubert /**
4025b9c547cSRui Paulo * ieee802_1x_kay_get_cipher_suite
4035b9c547cSRui Paulo */
4045b9c547cSRui Paulo static struct macsec_ciphersuite *
ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant * participant,const u8 * cs_id,unsigned int * idx)4055b9c547cSRui Paulo ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
4064bc52338SCy Schubert const u8 *cs_id, unsigned int *idx)
4075b9c547cSRui Paulo {
4085b9c547cSRui Paulo unsigned int i;
409780fb4a2SCy Schubert u64 cs;
410780fb4a2SCy Schubert be64 _cs;
411780fb4a2SCy Schubert
412780fb4a2SCy Schubert os_memcpy(&_cs, cs_id, CS_ID_LEN);
413780fb4a2SCy Schubert cs = be_to_host64(_cs);
4145b9c547cSRui Paulo
4155b9c547cSRui Paulo for (i = 0; i < CS_TABLE_SIZE; i++) {
4164bc52338SCy Schubert if (cipher_suite_tbl[i].id == cs) {
4174bc52338SCy Schubert *idx = i;
4185b9c547cSRui Paulo return &cipher_suite_tbl[i];
4195b9c547cSRui Paulo }
4204bc52338SCy Schubert }
4215b9c547cSRui Paulo
422780fb4a2SCy Schubert return NULL;
423780fb4a2SCy Schubert }
424780fb4a2SCy Schubert
425780fb4a2SCy Schubert
mka_sci_u64(struct ieee802_1x_mka_sci * sci)42685732ac8SCy Schubert u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
42785732ac8SCy Schubert {
42885732ac8SCy Schubert struct ieee802_1x_mka_sci tmp;
42985732ac8SCy Schubert
43085732ac8SCy Schubert os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
43185732ac8SCy Schubert tmp.port = sci->port;
43285732ac8SCy Schubert
43385732ac8SCy Schubert return *((u64 *) &tmp);
43485732ac8SCy Schubert }
43585732ac8SCy Schubert
43685732ac8SCy Schubert
sci_equal(const struct ieee802_1x_mka_sci * a,const struct ieee802_1x_mka_sci * b)437c1d255d3SCy Schubert static bool sci_equal(const struct ieee802_1x_mka_sci *a,
438780fb4a2SCy Schubert const struct ieee802_1x_mka_sci *b)
439780fb4a2SCy Schubert {
440780fb4a2SCy Schubert return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0;
441780fb4a2SCy Schubert }
442780fb4a2SCy Schubert
4435b9c547cSRui Paulo
4445b9c547cSRui Paulo /**
4455b9c547cSRui Paulo * ieee802_1x_kay_get_peer_sci
4465b9c547cSRui Paulo */
4475b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant * participant,const struct ieee802_1x_mka_sci * sci)4485b9c547cSRui Paulo ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
4495b9c547cSRui Paulo const struct ieee802_1x_mka_sci *sci)
4505b9c547cSRui Paulo {
4515b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
4525b9c547cSRui Paulo
4535b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
4545b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
455780fb4a2SCy Schubert if (sci_equal(&peer->sci, sci))
4565b9c547cSRui Paulo return peer;
4575b9c547cSRui Paulo }
4585b9c547cSRui Paulo
4595b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers,
4605b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
461780fb4a2SCy Schubert if (sci_equal(&peer->sci, sci))
4625b9c547cSRui Paulo return peer;
4635b9c547cSRui Paulo }
4645b9c547cSRui Paulo
4655b9c547cSRui Paulo return NULL;
4665b9c547cSRui Paulo }
4675b9c547cSRui Paulo
4685b9c547cSRui Paulo
46985732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
47085732ac8SCy Schubert
4715b9c547cSRui Paulo /**
4725b9c547cSRui Paulo * ieee802_1x_kay_init_receive_sa -
4735b9c547cSRui Paulo */
4745b9c547cSRui Paulo static struct receive_sa *
ieee802_1x_kay_init_receive_sa(struct receive_sc * psc,u8 an,u32 lowest_pn,struct data_key * key)4755b9c547cSRui Paulo ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
4765b9c547cSRui Paulo struct data_key *key)
4775b9c547cSRui Paulo {
4785b9c547cSRui Paulo struct receive_sa *psa;
4795b9c547cSRui Paulo
4805b9c547cSRui Paulo if (!psc || !key)
4815b9c547cSRui Paulo return NULL;
4825b9c547cSRui Paulo
4835b9c547cSRui Paulo psa = os_zalloc(sizeof(*psa));
4845b9c547cSRui Paulo if (!psa) {
4855b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
4865b9c547cSRui Paulo return NULL;
4875b9c547cSRui Paulo }
4885b9c547cSRui Paulo
48985732ac8SCy Schubert ieee802_1x_kay_use_data_key(key);
4905b9c547cSRui Paulo psa->pkey = key;
4915b9c547cSRui Paulo psa->lowest_pn = lowest_pn;
4925b9c547cSRui Paulo psa->next_pn = lowest_pn;
4935b9c547cSRui Paulo psa->an = an;
4945b9c547cSRui Paulo psa->sc = psc;
4955b9c547cSRui Paulo
4965b9c547cSRui Paulo os_get_time(&psa->created_time);
497c1d255d3SCy Schubert psa->in_use = false;
4985b9c547cSRui Paulo
4995b9c547cSRui Paulo dl_list_add(&psc->sa_list, &psa->list);
5005b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
5014bc52338SCy Schubert "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC",
50285732ac8SCy Schubert an, lowest_pn);
5035b9c547cSRui Paulo
5045b9c547cSRui Paulo return psa;
5055b9c547cSRui Paulo }
5065b9c547cSRui Paulo
5075b9c547cSRui Paulo
50885732ac8SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
50985732ac8SCy Schubert
5105b9c547cSRui Paulo /**
5115b9c547cSRui Paulo * ieee802_1x_kay_deinit_receive_sa -
5125b9c547cSRui Paulo */
ieee802_1x_kay_deinit_receive_sa(struct receive_sa * psa)5135b9c547cSRui Paulo static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
5145b9c547cSRui Paulo {
51585732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(psa->pkey);
5165b9c547cSRui Paulo psa->pkey = NULL;
5175b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
518780fb4a2SCy Schubert "KaY: Delete receive SA(an: %hhu) of SC",
519780fb4a2SCy Schubert psa->an);
5205b9c547cSRui Paulo dl_list_del(&psa->list);
5215b9c547cSRui Paulo os_free(psa);
5225b9c547cSRui Paulo }
5235b9c547cSRui Paulo
5245b9c547cSRui Paulo
5255b9c547cSRui Paulo /**
5265b9c547cSRui Paulo * ieee802_1x_kay_init_receive_sc -
5275b9c547cSRui Paulo */
5285b9c547cSRui Paulo static struct receive_sc *
ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci * psci)52985732ac8SCy Schubert ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
5305b9c547cSRui Paulo {
5315b9c547cSRui Paulo struct receive_sc *psc;
5325b9c547cSRui Paulo
5335b9c547cSRui Paulo if (!psci)
5345b9c547cSRui Paulo return NULL;
5355b9c547cSRui Paulo
5365b9c547cSRui Paulo psc = os_zalloc(sizeof(*psc));
5375b9c547cSRui Paulo if (!psc) {
5385b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
5395b9c547cSRui Paulo return NULL;
5405b9c547cSRui Paulo }
5415b9c547cSRui Paulo
5425b9c547cSRui Paulo os_memcpy(&psc->sci, psci, sizeof(psc->sci));
5435b9c547cSRui Paulo
5445b9c547cSRui Paulo os_get_time(&psc->created_time);
545c1d255d3SCy Schubert psc->receiving = false;
5465b9c547cSRui Paulo
5475b9c547cSRui Paulo dl_list_init(&psc->sa_list);
5484bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s",
5494bc52338SCy Schubert sci_txt(&psc->sci));
5505b9c547cSRui Paulo
5515b9c547cSRui Paulo return psc;
5525b9c547cSRui Paulo }
5535b9c547cSRui Paulo
5545b9c547cSRui Paulo
ieee802_1x_delete_receive_sa(struct ieee802_1x_kay * kay,struct receive_sa * sa)55585732ac8SCy Schubert static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
55685732ac8SCy Schubert struct receive_sa *sa)
55785732ac8SCy Schubert {
55885732ac8SCy Schubert secy_disable_receive_sa(kay, sa);
55985732ac8SCy Schubert secy_delete_receive_sa(kay, sa);
56085732ac8SCy Schubert ieee802_1x_kay_deinit_receive_sa(sa);
56185732ac8SCy Schubert }
56285732ac8SCy Schubert
56385732ac8SCy Schubert
5645b9c547cSRui Paulo /**
5655b9c547cSRui Paulo * ieee802_1x_kay_deinit_receive_sc -
5665b9c547cSRui Paulo **/
5675b9c547cSRui Paulo static void
ieee802_1x_kay_deinit_receive_sc(struct ieee802_1x_mka_participant * participant,struct receive_sc * psc)5685b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(
5695b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
5705b9c547cSRui Paulo {
5715b9c547cSRui Paulo struct receive_sa *psa, *pre_sa;
5725b9c547cSRui Paulo
57385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
5745b9c547cSRui Paulo dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
57585732ac8SCy Schubert list)
57685732ac8SCy Schubert ieee802_1x_delete_receive_sa(participant->kay, psa);
57785732ac8SCy Schubert
5785b9c547cSRui Paulo dl_list_del(&psc->list);
57985732ac8SCy Schubert secy_delete_receive_sc(participant->kay, psc);
5805b9c547cSRui Paulo os_free(psc);
5815b9c547cSRui Paulo }
5825b9c547cSRui Paulo
5835b9c547cSRui Paulo
ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer * peer)584780fb4a2SCy Schubert static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer)
585780fb4a2SCy Schubert {
5864bc52338SCy Schubert wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s",
5874bc52338SCy Schubert mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci));
588780fb4a2SCy Schubert }
589780fb4a2SCy Schubert
590780fb4a2SCy Schubert
5915b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_peer(const u8 * mi,u32 mn)592780fb4a2SCy Schubert ieee802_1x_kay_create_peer(const u8 *mi, u32 mn)
5935b9c547cSRui Paulo {
5945b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
5955b9c547cSRui Paulo
5965b9c547cSRui Paulo peer = os_zalloc(sizeof(*peer));
597780fb4a2SCy Schubert if (!peer) {
5985b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
5995b9c547cSRui Paulo return NULL;
6005b9c547cSRui Paulo }
6015b9c547cSRui Paulo
6025b9c547cSRui Paulo os_memcpy(peer->mi, mi, MI_LEN);
6035b9c547cSRui Paulo peer->mn = mn;
6045b9c547cSRui Paulo peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
605c1d255d3SCy Schubert peer->sak_used = false;
6064bc52338SCy Schubert peer->missing_sak_use_count = 0;
607780fb4a2SCy Schubert
608780fb4a2SCy Schubert return peer;
609780fb4a2SCy Schubert }
610780fb4a2SCy Schubert
611780fb4a2SCy Schubert
612780fb4a2SCy Schubert /**
613780fb4a2SCy Schubert * ieee802_1x_kay_create_live_peer
614780fb4a2SCy Schubert */
615780fb4a2SCy Schubert static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi,u32 mn)616780fb4a2SCy Schubert ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
617780fb4a2SCy Schubert const u8 *mi, u32 mn)
618780fb4a2SCy Schubert {
619780fb4a2SCy Schubert struct ieee802_1x_kay_peer *peer;
620780fb4a2SCy Schubert struct receive_sc *rxsc;
621780fb4a2SCy Schubert
622780fb4a2SCy Schubert peer = ieee802_1x_kay_create_peer(mi, mn);
623780fb4a2SCy Schubert if (!peer)
624780fb4a2SCy Schubert return NULL;
625780fb4a2SCy Schubert
6265b9c547cSRui Paulo os_memcpy(&peer->sci, &participant->current_peer_sci,
6275b9c547cSRui Paulo sizeof(peer->sci));
6285b9c547cSRui Paulo
62985732ac8SCy Schubert rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
630780fb4a2SCy Schubert if (!rxsc) {
631780fb4a2SCy Schubert os_free(peer);
6325b9c547cSRui Paulo return NULL;
633780fb4a2SCy Schubert }
6345b9c547cSRui Paulo
6354bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) {
6364bc52338SCy Schubert os_free(rxsc);
6374bc52338SCy Schubert os_free(peer);
6384bc52338SCy Schubert return NULL;
6394bc52338SCy Schubert }
640780fb4a2SCy Schubert dl_list_add(&participant->live_peers, &peer->list);
6415b9c547cSRui Paulo dl_list_add(&participant->rxsc_list, &rxsc->list);
6425b9c547cSRui Paulo
6435b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Live peer created");
644780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer);
6455b9c547cSRui Paulo
6465b9c547cSRui Paulo return peer;
6475b9c547cSRui Paulo }
6485b9c547cSRui Paulo
6495b9c547cSRui Paulo
6505b9c547cSRui Paulo /**
6515b9c547cSRui Paulo * ieee802_1x_kay_create_potential_peer
6525b9c547cSRui Paulo */
6535b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_potential_peer(struct ieee802_1x_mka_participant * participant,const u8 * mi,u32 mn)6545b9c547cSRui Paulo ieee802_1x_kay_create_potential_peer(
6555b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
6565b9c547cSRui Paulo {
6575b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
6585b9c547cSRui Paulo
659780fb4a2SCy Schubert peer = ieee802_1x_kay_create_peer(mi, mn);
660780fb4a2SCy Schubert if (!peer)
6615b9c547cSRui Paulo return NULL;
6625b9c547cSRui Paulo
6635b9c547cSRui Paulo dl_list_add(&participant->potential_peers, &peer->list);
6645b9c547cSRui Paulo
6654bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Potential peer created");
666780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer);
6675b9c547cSRui Paulo
6685b9c547cSRui Paulo return peer;
6695b9c547cSRui Paulo }
6705b9c547cSRui Paulo
6715b9c547cSRui Paulo
6725b9c547cSRui Paulo /**
6735b9c547cSRui Paulo * ieee802_1x_kay_move_live_peer
6745b9c547cSRui Paulo */
6755b9c547cSRui Paulo static struct ieee802_1x_kay_peer *
ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant * participant,u8 * mi,u32 mn)6765b9c547cSRui Paulo ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
6775b9c547cSRui Paulo u8 *mi, u32 mn)
6785b9c547cSRui Paulo {
6795b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
6805b9c547cSRui Paulo struct receive_sc *rxsc;
6815b9c547cSRui Paulo
682780fb4a2SCy Schubert peer = ieee802_1x_kay_get_potential_peer(participant, mi);
68385732ac8SCy Schubert if (!peer)
68485732ac8SCy Schubert return NULL;
685780fb4a2SCy Schubert
68685732ac8SCy Schubert rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
687780fb4a2SCy Schubert if (!rxsc)
688780fb4a2SCy Schubert return NULL;
6895b9c547cSRui Paulo
6905b9c547cSRui Paulo os_memcpy(&peer->sci, &participant->current_peer_sci,
6915b9c547cSRui Paulo sizeof(peer->sci));
6925b9c547cSRui Paulo peer->mn = mn;
6935b9c547cSRui Paulo peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
6945b9c547cSRui Paulo
6954bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer");
696780fb4a2SCy Schubert ieee802_1x_kay_dump_peer(peer);
6975b9c547cSRui Paulo
6985b9c547cSRui Paulo dl_list_del(&peer->list);
6994bc52338SCy Schubert if (secy_create_receive_sc(participant->kay, rxsc)) {
7004bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer");
7014bc52338SCy Schubert os_free(rxsc);
7024bc52338SCy Schubert os_free(peer);
7034bc52338SCy Schubert return NULL;
7044bc52338SCy Schubert }
7055b9c547cSRui Paulo dl_list_add_tail(&participant->live_peers, &peer->list);
7065b9c547cSRui Paulo
7075b9c547cSRui Paulo dl_list_add(&participant->rxsc_list, &rxsc->list);
7085b9c547cSRui Paulo
7095b9c547cSRui Paulo return peer;
7105b9c547cSRui Paulo }
7115b9c547cSRui Paulo
7125b9c547cSRui Paulo
7135b9c547cSRui Paulo
7145b9c547cSRui Paulo /**
7155b9c547cSRui Paulo * ieee802_1x_mka_basic_body_present -
7165b9c547cSRui Paulo */
717c1d255d3SCy Schubert static bool
ieee802_1x_mka_basic_body_present(struct ieee802_1x_mka_participant * participant)7185b9c547cSRui Paulo ieee802_1x_mka_basic_body_present(
7195b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
7205b9c547cSRui Paulo {
721c1d255d3SCy Schubert return true;
7225b9c547cSRui Paulo }
7235b9c547cSRui Paulo
7245b9c547cSRui Paulo
7255b9c547cSRui Paulo /**
7265b9c547cSRui Paulo * ieee802_1x_mka_basic_body_length -
7275b9c547cSRui Paulo */
7285b9c547cSRui Paulo static int
ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant * participant)7295b9c547cSRui Paulo ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
7305b9c547cSRui Paulo {
7315b9c547cSRui Paulo int length;
7325b9c547cSRui Paulo
7335b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_basic_body);
7345b9c547cSRui Paulo length += participant->ckn.len;
735780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length);
7365b9c547cSRui Paulo }
7375b9c547cSRui Paulo
7385b9c547cSRui Paulo
7395b9c547cSRui Paulo /**
7405b9c547cSRui Paulo * ieee802_1x_mka_encode_basic_body
7415b9c547cSRui Paulo */
7425b9c547cSRui Paulo static int
ieee802_1x_mka_encode_basic_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)7435b9c547cSRui Paulo ieee802_1x_mka_encode_basic_body(
7445b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
7455b9c547cSRui Paulo struct wpabuf *buf)
7465b9c547cSRui Paulo {
7475b9c547cSRui Paulo struct ieee802_1x_mka_basic_body *body;
7485b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay;
7494bc52338SCy Schubert unsigned int length = sizeof(struct ieee802_1x_mka_basic_body);
7505b9c547cSRui Paulo
7514bc52338SCy Schubert length += participant->ckn.len;
7524bc52338SCy Schubert body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length));
7535b9c547cSRui Paulo
7545b9c547cSRui Paulo body->version = kay->mka_version;
7555b9c547cSRui Paulo body->priority = kay->actor_priority;
7564bc52338SCy Schubert /* The Key Server flag is set if and only if the participant has not
7574bc52338SCy Schubert * decided that another participant is or will be the Key Server. */
7585b9c547cSRui Paulo if (participant->is_elected)
7595b9c547cSRui Paulo body->key_server = participant->is_key_server;
7605b9c547cSRui Paulo else
7615b9c547cSRui Paulo body->key_server = participant->can_be_key_server;
7625b9c547cSRui Paulo
7635b9c547cSRui Paulo body->macsec_desired = kay->macsec_desired;
764780fb4a2SCy Schubert body->macsec_capability = kay->macsec_capable;
7655b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN);
7665b9c547cSRui Paulo
7675b9c547cSRui Paulo os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
7685b9c547cSRui Paulo sizeof(kay->actor_sci.addr));
769780fb4a2SCy Schubert body->actor_sci.port = kay->actor_sci.port;
7705b9c547cSRui Paulo
7715b9c547cSRui Paulo os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
7725b9c547cSRui Paulo participant->mn = participant->mn + 1;
7735b9c547cSRui Paulo body->actor_mn = host_to_be32(participant->mn);
774780fb4a2SCy Schubert os_memcpy(body->algo_agility, kay->algo_agility,
7755b9c547cSRui Paulo sizeof(body->algo_agility));
7765b9c547cSRui Paulo
7775b9c547cSRui Paulo os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
7785b9c547cSRui Paulo
7795b9c547cSRui Paulo ieee802_1x_mka_dump_basic_body(body);
7805b9c547cSRui Paulo
7815b9c547cSRui Paulo return 0;
7825b9c547cSRui Paulo }
7835b9c547cSRui Paulo
7845b9c547cSRui Paulo
785c1d255d3SCy Schubert static bool
reset_participant_mi(struct ieee802_1x_mka_participant * participant)786780fb4a2SCy Schubert reset_participant_mi(struct ieee802_1x_mka_participant *participant)
787780fb4a2SCy Schubert {
788780fb4a2SCy Schubert if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
789c1d255d3SCy Schubert return false;
790780fb4a2SCy Schubert participant->mn = 0;
791780fb4a2SCy Schubert
792c1d255d3SCy Schubert return true;
793780fb4a2SCy Schubert }
794780fb4a2SCy Schubert
795780fb4a2SCy Schubert
7965b9c547cSRui Paulo /**
7975b9c547cSRui Paulo * ieee802_1x_mka_decode_basic_body -
7985b9c547cSRui Paulo */
7995b9c547cSRui Paulo static struct ieee802_1x_mka_participant *
ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay * kay,const u8 * mka_msg,size_t msg_len)8005b9c547cSRui Paulo ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
8015b9c547cSRui Paulo size_t msg_len)
8025b9c547cSRui Paulo {
8035b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
8045b9c547cSRui Paulo const struct ieee802_1x_mka_basic_body *body;
8055b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
80685732ac8SCy Schubert size_t ckn_len;
80785732ac8SCy Schubert size_t body_len;
8085b9c547cSRui Paulo
8095b9c547cSRui Paulo body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
8105b9c547cSRui Paulo
8115b9c547cSRui Paulo if (body->version > MKA_VERSION_ID) {
8125b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
8134bc52338SCy Schubert "KaY: Peer's version(%d) greater than MKA current version(%d)",
8145b9c547cSRui Paulo body->version, MKA_VERSION_ID);
8155b9c547cSRui Paulo }
8165b9c547cSRui Paulo if (kay->is_obliged_key_server && body->key_server) {
8174bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server");
8185b9c547cSRui Paulo return NULL;
8195b9c547cSRui Paulo }
8205b9c547cSRui Paulo
82185732ac8SCy Schubert body_len = get_mka_param_body_len(body);
82285732ac8SCy Schubert if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
82385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
82485732ac8SCy Schubert body_len);
82585732ac8SCy Schubert return NULL;
82685732ac8SCy Schubert }
82785732ac8SCy Schubert ckn_len = body_len -
82885732ac8SCy Schubert (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
82985732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
8305b9c547cSRui Paulo if (!participant) {
8314bc52338SCy Schubert wpa_printf(MSG_DEBUG,
8324bc52338SCy Schubert "KaY: Peer is not included in my CA - ignore MKPDU");
8335b9c547cSRui Paulo return NULL;
8345b9c547cSRui Paulo }
8355b9c547cSRui Paulo
8365b9c547cSRui Paulo /* If the peer's MI is my MI, I will choose new MI */
8375b9c547cSRui Paulo if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
838780fb4a2SCy Schubert if (!reset_participant_mi(participant))
8395b9c547cSRui Paulo return NULL;
8404bc52338SCy Schubert wpa_printf(MSG_DEBUG,
8414bc52338SCy Schubert "KaY: Peer using my MI - selected a new random MI: %s",
8424bc52338SCy Schubert mi_txt(participant->mi));
8435b9c547cSRui Paulo }
8445b9c547cSRui Paulo
8455b9c547cSRui Paulo os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
846780fb4a2SCy Schubert participant->current_peer_id.mn = body->actor_mn;
8475b9c547cSRui Paulo os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
8485b9c547cSRui Paulo sizeof(participant->current_peer_sci.addr));
849780fb4a2SCy Schubert participant->current_peer_sci.port = body->actor_sci.port;
8505b9c547cSRui Paulo
8515b9c547cSRui Paulo /* handler peer */
8525b9c547cSRui Paulo peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
8535b9c547cSRui Paulo if (!peer) {
8544bc52338SCy Schubert /* Check duplicated SCI
8554bc52338SCy Schubert *
8564bc52338SCy Schubert * A duplicated SCI indicates either an active attacker or
8574bc52338SCy Schubert * a valid peer whose MI is being changed. The latter scenario
8584bc52338SCy Schubert * is more likely because to have gotten this far the received
8594bc52338SCy Schubert * MKPDU must have had a valid ICV, indicating the peer holds
8604bc52338SCy Schubert * the same CAK as our participant.
8614bc52338SCy Schubert *
8624bc52338SCy Schubert * Before creating a new peer object for the new MI we must
8634bc52338SCy Schubert * clean up the resources (SCs and SAs) associated with the
8644bc52338SCy Schubert * old peer. An easy way to do this is to ignore MKPDUs with
8654bc52338SCy Schubert * the new MI's for now and just wait for the old peer to
8664bc52338SCy Schubert * time out and clean itself up (within MKA_LIFE_TIME).
8674bc52338SCy Schubert *
8684bc52338SCy Schubert * This method is preferable to deleting the old peer here
8694bc52338SCy Schubert * and now and continuing on with processing because if this
8704bc52338SCy Schubert * MKPDU is from an attacker it's better to ignore the MKPDU
8714bc52338SCy Schubert * than to process it (and delete a valid peer as well).
8725b9c547cSRui Paulo */
8735b9c547cSRui Paulo peer = ieee802_1x_kay_get_peer_sci(participant,
8745b9c547cSRui Paulo &body->actor_sci);
8755b9c547cSRui Paulo if (peer) {
8764bc52338SCy Schubert time_t new_expire;
8774bc52338SCy Schubert
8785b9c547cSRui Paulo wpa_printf(MSG_WARNING,
8794bc52338SCy Schubert "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU");
8804bc52338SCy Schubert /* Reduce timeout to speed up this process but left the
8814bc52338SCy Schubert * chance for old one to prove aliveness. */
8824bc52338SCy Schubert new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000;
8834bc52338SCy Schubert if (peer->expire > new_expire)
8844bc52338SCy Schubert peer->expire = new_expire;
8854bc52338SCy Schubert return NULL;
8865b9c547cSRui Paulo }
8875b9c547cSRui Paulo
8885b9c547cSRui Paulo peer = ieee802_1x_kay_create_potential_peer(
8895b9c547cSRui Paulo participant, body->actor_mi,
8905b9c547cSRui Paulo be_to_host32(body->actor_mn));
8914bc52338SCy Schubert if (!peer) {
8924bc52338SCy Schubert wpa_printf(MSG_DEBUG,
8934bc52338SCy Schubert "KaY: No potential peer entry found - ignore MKPDU");
8945b9c547cSRui Paulo return NULL;
8954bc52338SCy Schubert }
8965b9c547cSRui Paulo
8975b9c547cSRui Paulo peer->macsec_desired = body->macsec_desired;
898780fb4a2SCy Schubert peer->macsec_capability = body->macsec_capability;
899c1d255d3SCy Schubert peer->is_key_server = body->key_server;
9005b9c547cSRui Paulo peer->key_server_priority = body->priority;
9015b9c547cSRui Paulo } else if (peer->mn < be_to_host32(body->actor_mn)) {
9025b9c547cSRui Paulo peer->mn = be_to_host32(body->actor_mn);
9035b9c547cSRui Paulo peer->macsec_desired = body->macsec_desired;
904780fb4a2SCy Schubert peer->macsec_capability = body->macsec_capability;
905c1d255d3SCy Schubert peer->is_key_server = body->key_server;
9065b9c547cSRui Paulo peer->key_server_priority = body->priority;
9075b9c547cSRui Paulo } else {
9084bc52338SCy Schubert wpa_printf(MSG_WARNING,
9094bc52338SCy Schubert "KaY: The peer MN did not increase - ignore MKPDU");
9105b9c547cSRui Paulo return NULL;
9115b9c547cSRui Paulo }
9125b9c547cSRui Paulo
9135b9c547cSRui Paulo return participant;
9145b9c547cSRui Paulo }
9155b9c547cSRui Paulo
9165b9c547cSRui Paulo
9175b9c547cSRui Paulo /**
9185b9c547cSRui Paulo * ieee802_1x_mka_live_peer_body_present
9195b9c547cSRui Paulo */
920c1d255d3SCy Schubert static bool
ieee802_1x_mka_live_peer_body_present(struct ieee802_1x_mka_participant * participant)9215b9c547cSRui Paulo ieee802_1x_mka_live_peer_body_present(
9225b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
9235b9c547cSRui Paulo {
9245b9c547cSRui Paulo return !dl_list_empty(&participant->live_peers);
9255b9c547cSRui Paulo }
9265b9c547cSRui Paulo
9275b9c547cSRui Paulo
9285b9c547cSRui Paulo /**
9295b9c547cSRui Paulo * ieee802_1x_kay_get_live_peer_length
9305b9c547cSRui Paulo */
9315b9c547cSRui Paulo static int
ieee802_1x_mka_get_live_peer_length(struct ieee802_1x_mka_participant * participant)9325b9c547cSRui Paulo ieee802_1x_mka_get_live_peer_length(
9335b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
9345b9c547cSRui Paulo {
9355b9c547cSRui Paulo int len = MKA_HDR_LEN;
9365b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
9375b9c547cSRui Paulo
9385b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
9395b9c547cSRui Paulo struct ieee802_1x_kay_peer, list)
9405b9c547cSRui Paulo len += sizeof(struct ieee802_1x_mka_peer_id);
9415b9c547cSRui Paulo
942780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(len);
9435b9c547cSRui Paulo }
9445b9c547cSRui Paulo
9455b9c547cSRui Paulo
9465b9c547cSRui Paulo /**
9475b9c547cSRui Paulo * ieee802_1x_mka_encode_live_peer_body -
9485b9c547cSRui Paulo */
9495b9c547cSRui Paulo static int
ieee802_1x_mka_encode_live_peer_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)9505b9c547cSRui Paulo ieee802_1x_mka_encode_live_peer_body(
9515b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
9525b9c547cSRui Paulo struct wpabuf *buf)
9535b9c547cSRui Paulo {
9545b9c547cSRui Paulo struct ieee802_1x_mka_peer_body *body;
9555b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
9565b9c547cSRui Paulo unsigned int length;
9575b9c547cSRui Paulo struct ieee802_1x_mka_peer_id *body_peer;
9585b9c547cSRui Paulo
9595b9c547cSRui Paulo length = ieee802_1x_mka_get_live_peer_length(participant);
9605b9c547cSRui Paulo body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
9615b9c547cSRui Paulo
9625b9c547cSRui Paulo body->type = MKA_LIVE_PEER_LIST;
9635b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN);
9645b9c547cSRui Paulo
9655b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
9665b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
9675b9c547cSRui Paulo body_peer = wpabuf_put(buf,
9685b9c547cSRui Paulo sizeof(struct ieee802_1x_mka_peer_id));
9695b9c547cSRui Paulo os_memcpy(body_peer->mi, peer->mi, MI_LEN);
9705b9c547cSRui Paulo body_peer->mn = host_to_be32(peer->mn);
9715b9c547cSRui Paulo }
9725b9c547cSRui Paulo
9735b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(body);
9745b9c547cSRui Paulo return 0;
9755b9c547cSRui Paulo }
9765b9c547cSRui Paulo
9775b9c547cSRui Paulo /**
9785b9c547cSRui Paulo * ieee802_1x_mka_potential_peer_body_present
9795b9c547cSRui Paulo */
980c1d255d3SCy Schubert static bool
ieee802_1x_mka_potential_peer_body_present(struct ieee802_1x_mka_participant * participant)9815b9c547cSRui Paulo ieee802_1x_mka_potential_peer_body_present(
9825b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
9835b9c547cSRui Paulo {
9845b9c547cSRui Paulo return !dl_list_empty(&participant->potential_peers);
9855b9c547cSRui Paulo }
9865b9c547cSRui Paulo
9875b9c547cSRui Paulo
9885b9c547cSRui Paulo /**
9895b9c547cSRui Paulo * ieee802_1x_kay_get_potential_peer_length
9905b9c547cSRui Paulo */
9915b9c547cSRui Paulo static int
ieee802_1x_mka_get_potential_peer_length(struct ieee802_1x_mka_participant * participant)9925b9c547cSRui Paulo ieee802_1x_mka_get_potential_peer_length(
9935b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
9945b9c547cSRui Paulo {
9955b9c547cSRui Paulo int len = MKA_HDR_LEN;
9965b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
9975b9c547cSRui Paulo
9985b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers,
9995b9c547cSRui Paulo struct ieee802_1x_kay_peer, list)
10005b9c547cSRui Paulo len += sizeof(struct ieee802_1x_mka_peer_id);
10015b9c547cSRui Paulo
1002780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(len);
10035b9c547cSRui Paulo }
10045b9c547cSRui Paulo
10055b9c547cSRui Paulo
10065b9c547cSRui Paulo /**
10075b9c547cSRui Paulo * ieee802_1x_mka_encode_potential_peer_body -
10085b9c547cSRui Paulo */
10095b9c547cSRui Paulo static int
ieee802_1x_mka_encode_potential_peer_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)10105b9c547cSRui Paulo ieee802_1x_mka_encode_potential_peer_body(
10115b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
10125b9c547cSRui Paulo struct wpabuf *buf)
10135b9c547cSRui Paulo {
10145b9c547cSRui Paulo struct ieee802_1x_mka_peer_body *body;
10155b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
10165b9c547cSRui Paulo unsigned int length;
10175b9c547cSRui Paulo struct ieee802_1x_mka_peer_id *body_peer;
10185b9c547cSRui Paulo
10195b9c547cSRui Paulo length = ieee802_1x_mka_get_potential_peer_length(participant);
10205b9c547cSRui Paulo body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
10215b9c547cSRui Paulo
10225b9c547cSRui Paulo body->type = MKA_POTENTIAL_PEER_LIST;
10235b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN);
10245b9c547cSRui Paulo
10255b9c547cSRui Paulo dl_list_for_each(peer, &participant->potential_peers,
10265b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
10275b9c547cSRui Paulo body_peer = wpabuf_put(buf,
10285b9c547cSRui Paulo sizeof(struct ieee802_1x_mka_peer_id));
10295b9c547cSRui Paulo os_memcpy(body_peer->mi, peer->mi, MI_LEN);
10305b9c547cSRui Paulo body_peer->mn = host_to_be32(peer->mn);
10315b9c547cSRui Paulo }
10325b9c547cSRui Paulo
10335b9c547cSRui Paulo ieee802_1x_mka_dump_peer_body(body);
10345b9c547cSRui Paulo return 0;
10355b9c547cSRui Paulo }
10365b9c547cSRui Paulo
10375b9c547cSRui Paulo
10385b9c547cSRui Paulo /**
10395b9c547cSRui Paulo * ieee802_1x_mka_i_in_peerlist -
10405b9c547cSRui Paulo */
1041c1d255d3SCy Schubert static bool
ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)10425b9c547cSRui Paulo ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
10435b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
10445b9c547cSRui Paulo {
10455b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
10465b9c547cSRui Paulo size_t body_len;
10475b9c547cSRui Paulo size_t left_len;
1048780fb4a2SCy Schubert u8 body_type;
10495b9c547cSRui Paulo const u8 *pos;
10505b9c547cSRui Paulo size_t i;
10515b9c547cSRui Paulo
1052780fb4a2SCy Schubert for (pos = mka_msg, left_len = msg_len;
1053780fb4a2SCy Schubert left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
10544bc52338SCy Schubert left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN,
10554bc52338SCy Schubert pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) {
10565b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos;
10575b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
10585b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr);
10595b9c547cSRui Paulo
106085732ac8SCy Schubert if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
10615b9c547cSRui Paulo wpa_printf(MSG_ERROR,
1062780fb4a2SCy 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",
1063780fb4a2SCy Schubert left_len, MKA_HDR_LEN,
106485732ac8SCy Schubert MKA_ALIGN_LENGTH(body_len),
106585732ac8SCy Schubert DEFAULT_ICV_LEN);
1066c1d255d3SCy Schubert return false;
10675b9c547cSRui Paulo }
10685b9c547cSRui Paulo
106985732ac8SCy Schubert if (body_type != MKA_LIVE_PEER_LIST &&
107085732ac8SCy Schubert body_type != MKA_POTENTIAL_PEER_LIST)
107185732ac8SCy Schubert continue;
107285732ac8SCy Schubert
10735b9c547cSRui Paulo if ((body_len % 16) != 0) {
10745b9c547cSRui Paulo wpa_printf(MSG_ERROR,
1075780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1076780fb4a2SCy Schubert body_len);
1077780fb4a2SCy Schubert continue;
10785b9c547cSRui Paulo }
10795b9c547cSRui Paulo
108085732ac8SCy Schubert ieee802_1x_mka_dump_peer_body(
108185732ac8SCy Schubert (struct ieee802_1x_mka_peer_body *)pos);
108285732ac8SCy Schubert
1083780fb4a2SCy Schubert for (i = 0; i < body_len;
1084780fb4a2SCy Schubert i += sizeof(struct ieee802_1x_mka_peer_id)) {
1085780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi;
10865b9c547cSRui Paulo
1087780fb4a2SCy Schubert peer_mi = (const struct ieee802_1x_mka_peer_id *)
1088780fb4a2SCy Schubert (pos + MKA_HDR_LEN + i);
1089780fb4a2SCy Schubert if (os_memcmp(peer_mi->mi, participant->mi,
10904bc52338SCy Schubert MI_LEN) == 0) {
10914bc52338SCy Schubert u32 mn = be_to_host32(peer_mi->mn);
10924bc52338SCy Schubert
10934bc52338SCy Schubert wpa_printf(MSG_DEBUG,
10944bc52338SCy Schubert "KaY: My MI - received MN %u, most recently transmitted MN %u",
10954bc52338SCy Schubert mn, participant->mn);
1096206b73d0SCy Schubert /* IEEE Std 802.1X-2010 is not exactly clear
1097206b73d0SCy Schubert * which values of MN should be accepted here.
1098206b73d0SCy Schubert * It uses "acceptably recent MN" language
1099206b73d0SCy Schubert * without defining what would be acceptable
1100206b73d0SCy Schubert * recent. For now, allow the last two used MN
1101206b73d0SCy Schubert * values (i.e., peer having copied my MI,MN
1102206b73d0SCy Schubert * from either of the last two MKPDUs that I
1103206b73d0SCy Schubert * have sent). */
1104206b73d0SCy Schubert if (mn == participant->mn ||
1105206b73d0SCy Schubert (participant->mn > 1 &&
1106206b73d0SCy Schubert mn == participant->mn - 1))
1107c1d255d3SCy Schubert return true;
1108780fb4a2SCy Schubert }
11095b9c547cSRui Paulo }
11104bc52338SCy Schubert }
11115b9c547cSRui Paulo
1112c1d255d3SCy Schubert return false;
11135b9c547cSRui Paulo }
11145b9c547cSRui Paulo
11155b9c547cSRui Paulo
11165b9c547cSRui Paulo /**
11175b9c547cSRui Paulo * ieee802_1x_mka_decode_live_peer_body -
11185b9c547cSRui Paulo */
ieee802_1x_mka_decode_live_peer_body(struct ieee802_1x_mka_participant * participant,const u8 * peer_msg,size_t msg_len)11195b9c547cSRui Paulo static int ieee802_1x_mka_decode_live_peer_body(
11205b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
11215b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len)
11225b9c547cSRui Paulo {
11235b9c547cSRui Paulo const struct ieee802_1x_mka_hdr *hdr;
11245b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
11255b9c547cSRui Paulo size_t body_len;
11265b9c547cSRui Paulo size_t i;
1127c1d255d3SCy Schubert bool is_included;
11285b9c547cSRui Paulo
11295b9c547cSRui Paulo is_included = ieee802_1x_kay_is_in_live_peer(
11305b9c547cSRui Paulo participant, participant->current_peer_id.mi);
11315b9c547cSRui Paulo
11325b9c547cSRui Paulo hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11335b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
1134780fb4a2SCy Schubert if (body_len % 16 != 0) {
1135780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
1136780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1137780fb4a2SCy Schubert body_len);
1138780fb4a2SCy Schubert return -1;
1139780fb4a2SCy Schubert }
11405b9c547cSRui Paulo
1141780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1142780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi;
1143780fb4a2SCy Schubert u32 peer_mn;
1144780fb4a2SCy Schubert
1145780fb4a2SCy Schubert peer_mi = (const struct ieee802_1x_mka_peer_id *)
1146780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i);
1147780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn);
11485b9c547cSRui Paulo
11495b9c547cSRui Paulo /* it is myself */
11505b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
11515b9c547cSRui Paulo /* My message id is used by other participant */
1152780fb4a2SCy Schubert if (peer_mn > participant->mn &&
1153780fb4a2SCy Schubert !reset_participant_mi(participant))
1154780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
11555b9c547cSRui Paulo continue;
11565b9c547cSRui Paulo }
1157780fb4a2SCy Schubert
11585b9c547cSRui Paulo if (!is_included)
11595b9c547cSRui Paulo continue;
11605b9c547cSRui Paulo
1161780fb4a2SCy Schubert peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
1162780fb4a2SCy Schubert if (peer) {
11635b9c547cSRui Paulo peer->mn = peer_mn;
1164780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_potential_peer(
1165780fb4a2SCy Schubert participant, peer_mi->mi, peer_mn)) {
11665b9c547cSRui Paulo return -1;
11675b9c547cSRui Paulo }
11685b9c547cSRui Paulo }
11695b9c547cSRui Paulo
11705b9c547cSRui Paulo return 0;
11715b9c547cSRui Paulo }
11725b9c547cSRui Paulo
11735b9c547cSRui Paulo
11745b9c547cSRui Paulo /**
11755b9c547cSRui Paulo * ieee802_1x_mka_decode_potential_peer_body -
11765b9c547cSRui Paulo */
11775b9c547cSRui Paulo static int
ieee802_1x_mka_decode_potential_peer_body(struct ieee802_1x_mka_participant * participant,const u8 * peer_msg,size_t msg_len)11785b9c547cSRui Paulo ieee802_1x_mka_decode_potential_peer_body(
11795b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
11805b9c547cSRui Paulo const u8 *peer_msg, size_t msg_len)
11815b9c547cSRui Paulo {
1182780fb4a2SCy Schubert const struct ieee802_1x_mka_hdr *hdr;
11835b9c547cSRui Paulo size_t body_len;
11845b9c547cSRui Paulo size_t i;
11855b9c547cSRui Paulo
1186780fb4a2SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
11875b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
1188780fb4a2SCy Schubert if (body_len % 16 != 0) {
1189780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
1190780fb4a2SCy Schubert "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
1191780fb4a2SCy Schubert body_len);
1192780fb4a2SCy Schubert return -1;
1193780fb4a2SCy Schubert }
11945b9c547cSRui Paulo
1195780fb4a2SCy Schubert for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
1196780fb4a2SCy Schubert const struct ieee802_1x_mka_peer_id *peer_mi;
1197780fb4a2SCy Schubert u32 peer_mn;
1198780fb4a2SCy Schubert
1199780fb4a2SCy Schubert peer_mi = (struct ieee802_1x_mka_peer_id *)
1200780fb4a2SCy Schubert (peer_msg + MKA_HDR_LEN + i);
1201780fb4a2SCy Schubert peer_mn = be_to_host32(peer_mi->mn);
12025b9c547cSRui Paulo
12035b9c547cSRui Paulo /* it is myself */
12045b9c547cSRui Paulo if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
12055b9c547cSRui Paulo /* My message id is used by other participant */
1206780fb4a2SCy Schubert if (peer_mn > participant->mn &&
1207780fb4a2SCy Schubert !reset_participant_mi(participant))
1208*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update MI");
12095b9c547cSRui Paulo continue;
12105b9c547cSRui Paulo }
12115b9c547cSRui Paulo }
12125b9c547cSRui Paulo
12135b9c547cSRui Paulo return 0;
12145b9c547cSRui Paulo }
12155b9c547cSRui Paulo
12165b9c547cSRui Paulo
12175b9c547cSRui Paulo /**
12185b9c547cSRui Paulo * ieee802_1x_mka_sak_use_body_present
12195b9c547cSRui Paulo */
1220c1d255d3SCy Schubert static bool
ieee802_1x_mka_sak_use_body_present(struct ieee802_1x_mka_participant * participant)12215b9c547cSRui Paulo ieee802_1x_mka_sak_use_body_present(
12225b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
12235b9c547cSRui Paulo {
1224780fb4a2SCy Schubert return participant->to_use_sak;
12255b9c547cSRui Paulo }
12265b9c547cSRui Paulo
12275b9c547cSRui Paulo
12285b9c547cSRui Paulo /**
12295b9c547cSRui Paulo * ieee802_1x_mka_get_sak_use_length
12305b9c547cSRui Paulo */
12315b9c547cSRui Paulo static int
ieee802_1x_mka_get_sak_use_length(struct ieee802_1x_mka_participant * participant)12325b9c547cSRui Paulo ieee802_1x_mka_get_sak_use_length(
12335b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
12345b9c547cSRui Paulo {
12355b9c547cSRui Paulo int length = MKA_HDR_LEN;
12365b9c547cSRui Paulo
12375b9c547cSRui Paulo if (participant->kay->macsec_desired && participant->advised_desired)
12385b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_sak_use_body);
12395b9c547cSRui Paulo
1240780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length);
12415b9c547cSRui Paulo }
12425b9c547cSRui Paulo
12435b9c547cSRui Paulo
12445b9c547cSRui Paulo /**
12454bc52338SCy Schubert * ieee802_1x_mka_get_lpn
12465b9c547cSRui Paulo */
12475b9c547cSRui Paulo static u32
ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant * principal,struct ieee802_1x_mka_ki * ki)12485b9c547cSRui Paulo ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
12495b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki)
12505b9c547cSRui Paulo {
12514bc52338SCy Schubert struct transmit_sa *txsa;
12525b9c547cSRui Paulo u32 lpn = 0;
12535b9c547cSRui Paulo
12544bc52338SCy Schubert dl_list_for_each(txsa, &principal->txsc->sa_list,
12554bc52338SCy Schubert struct transmit_sa, list) {
12564bc52338SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
12574bc52338SCy Schubert /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
12584bc52338SCy Schubert * MKA to communicate the lowest PN used for
12594bc52338SCy Schubert * transmission with the SAK within the last two
12604bc52338SCy Schubert * seconds". Achieve this 2 second delay by setting the
12614bc52338SCy Schubert * lpn using the transmit next PN (i.e., txsa->next_pn)
12624bc52338SCy Schubert * that was read last time here (i.e., mka_hello_time
12634bc52338SCy Schubert * 2 seconds ago).
12644bc52338SCy Schubert *
12654bc52338SCy Schubert * The lowest acceptable PN is the same as the last
12664bc52338SCy Schubert * transmitted PN, which is one less than the next
12674bc52338SCy Schubert * transmit PN.
12684bc52338SCy Schubert *
12694bc52338SCy Schubert * NOTE: This method only works if mka_hello_time is 2s.
12704bc52338SCy Schubert */
12714bc52338SCy Schubert lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
12725b9c547cSRui Paulo
12734bc52338SCy Schubert /* Now read the current transmit next PN for use next
12744bc52338SCy Schubert * time through. */
12754bc52338SCy Schubert secy_get_transmit_next_pn(principal->kay, txsa);
12765b9c547cSRui Paulo break;
12775b9c547cSRui Paulo }
12785b9c547cSRui Paulo }
12795b9c547cSRui Paulo
12805b9c547cSRui Paulo if (lpn == 0)
12815b9c547cSRui Paulo lpn = 1;
12825b9c547cSRui Paulo
12835b9c547cSRui Paulo return lpn;
12845b9c547cSRui Paulo }
12855b9c547cSRui Paulo
12865b9c547cSRui Paulo
12875b9c547cSRui Paulo /**
12885b9c547cSRui Paulo * ieee802_1x_mka_encode_sak_use_body -
12895b9c547cSRui Paulo */
12905b9c547cSRui Paulo static int
ieee802_1x_mka_encode_sak_use_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)12915b9c547cSRui Paulo ieee802_1x_mka_encode_sak_use_body(
12925b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
12935b9c547cSRui Paulo struct wpabuf *buf)
12945b9c547cSRui Paulo {
12955b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body;
1296780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay;
12975b9c547cSRui Paulo unsigned int length;
1298c1d255d3SCy Schubert u32 olpn, llpn;
12995b9c547cSRui Paulo
13005b9c547cSRui Paulo length = ieee802_1x_mka_get_sak_use_length(participant);
1301780fb4a2SCy Schubert body = wpabuf_put(buf, length);
13025b9c547cSRui Paulo
13035b9c547cSRui Paulo body->type = MKA_SAK_USE;
13045b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN);
13055b9c547cSRui Paulo
13065b9c547cSRui Paulo if (length == MKA_HDR_LEN) {
1307c1d255d3SCy Schubert body->ptx = true;
1308c1d255d3SCy Schubert body->prx = true;
13095b9c547cSRui Paulo body->lan = 0;
1310c1d255d3SCy Schubert body->lrx = false;
1311c1d255d3SCy Schubert body->ltx = false;
1312c1d255d3SCy Schubert body->delay_protect = false;
13135b9c547cSRui Paulo return 0;
13145b9c547cSRui Paulo }
13155b9c547cSRui Paulo
13164bc52338SCy Schubert /* data delay protect */
13174bc52338SCy Schubert body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
1318c1d255d3SCy Schubert /* lowest accept packet numbers */
1319c1d255d3SCy Schubert olpn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
1320c1d255d3SCy Schubert body->olpn = host_to_be32(olpn);
1321c1d255d3SCy Schubert llpn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
1322c1d255d3SCy Schubert body->llpn = host_to_be32(llpn);
1323c1d255d3SCy Schubert if (participant->is_key_server) {
1324c1d255d3SCy Schubert /* The CP will spend most of it's time in RETIRE where only
1325c1d255d3SCy Schubert * the old key is populated. Therefore we should be checking
1326c1d255d3SCy Schubert * the OLPN most of the time.
1327c1d255d3SCy Schubert */
1328c1d255d3SCy Schubert if (participant->lrx) {
1329c1d255d3SCy Schubert if (llpn > kay->pn_exhaustion) {
1330c1d255d3SCy Schubert wpa_printf(MSG_WARNING,
1331c1d255d3SCy Schubert "KaY: My LLPN exhaustion");
1332c1d255d3SCy Schubert participant->new_sak = true;
13335b9c547cSRui Paulo }
1334c1d255d3SCy Schubert } else {
1335c1d255d3SCy Schubert if (olpn > kay->pn_exhaustion) {
1336c1d255d3SCy Schubert wpa_printf(MSG_WARNING,
1337c1d255d3SCy Schubert "KaY: My OLPN exhaustion");
1338c1d255d3SCy Schubert participant->new_sak = true;
1339c1d255d3SCy Schubert }
1340c1d255d3SCy Schubert }
1341c1d255d3SCy Schubert }
13425b9c547cSRui Paulo
13435b9c547cSRui Paulo /* plain tx, plain rx */
1344780fb4a2SCy Schubert body->ptx = !kay->macsec_protect;
1345780fb4a2SCy Schubert body->prx = kay->macsec_validate != Strict;
13465b9c547cSRui Paulo
13475b9c547cSRui Paulo /* latest key: rx, tx, key server member identifier key number */
13485b9c547cSRui Paulo body->lan = participant->lan;
1349780fb4a2SCy Schubert os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi));
13505b9c547cSRui Paulo body->lkn = host_to_be32(participant->lki.kn);
13515b9c547cSRui Paulo body->lrx = participant->lrx;
13525b9c547cSRui Paulo body->ltx = participant->ltx;
13535b9c547cSRui Paulo
13545b9c547cSRui Paulo /* old key: rx, tx, key server member identifier key number */
13555b9c547cSRui Paulo body->oan = participant->oan;
13565b9c547cSRui Paulo if (participant->oki.kn != participant->lki.kn &&
13575b9c547cSRui Paulo participant->oki.kn != 0) {
1358c1d255d3SCy Schubert body->otx = true;
1359c1d255d3SCy Schubert body->orx = true;
13605b9c547cSRui Paulo os_memcpy(body->osrv_mi, participant->oki.mi,
13615b9c547cSRui Paulo sizeof(body->osrv_mi));
13625b9c547cSRui Paulo body->okn = host_to_be32(participant->oki.kn);
13635b9c547cSRui Paulo } else {
1364c1d255d3SCy Schubert body->otx = false;
1365c1d255d3SCy Schubert body->orx = false;
13665b9c547cSRui Paulo }
13675b9c547cSRui Paulo
13685b9c547cSRui Paulo /* set CP's variable */
13695b9c547cSRui Paulo if (body->ltx) {
1370c1d255d3SCy Schubert kay->tx_enable = true;
1371c1d255d3SCy Schubert kay->port_enable = true;
13725b9c547cSRui Paulo }
1373780fb4a2SCy Schubert if (body->lrx)
1374c1d255d3SCy Schubert kay->rx_enable = true;
13755b9c547cSRui Paulo
13765b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body);
13775b9c547cSRui Paulo return 0;
13785b9c547cSRui Paulo }
13795b9c547cSRui Paulo
13805b9c547cSRui Paulo
13815b9c547cSRui Paulo /**
13825b9c547cSRui Paulo * ieee802_1x_mka_decode_sak_use_body -
13835b9c547cSRui Paulo */
13845b9c547cSRui Paulo static int
ieee802_1x_mka_decode_sak_use_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)13855b9c547cSRui Paulo ieee802_1x_mka_decode_sak_use_body(
13865b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
13875b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
13885b9c547cSRui Paulo {
13895b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
13905b9c547cSRui Paulo struct ieee802_1x_mka_sak_use_body *body;
13915b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
13925b9c547cSRui Paulo struct data_key *sa_key = NULL;
13935b9c547cSRui Paulo size_t body_len;
13945b9c547cSRui Paulo struct ieee802_1x_mka_ki ki;
13955b9c547cSRui Paulo u32 lpn;
1396780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay;
1397c1d255d3SCy Schubert u32 olpn, llpn;
13985b9c547cSRui Paulo
13995b9c547cSRui Paulo if (!participant->principal) {
14005b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
14015b9c547cSRui Paulo return -1;
14025b9c547cSRui Paulo }
14035b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant,
14045b9c547cSRui Paulo participant->current_peer_id.mi);
14055b9c547cSRui Paulo if (!peer) {
14064bc52338SCy Schubert wpa_printf(MSG_WARNING,
14074bc52338SCy Schubert "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set",
14084bc52338SCy Schubert mi_txt(participant->current_peer_id.mi));
14095b9c547cSRui Paulo return -1;
14105b9c547cSRui Paulo }
14115b9c547cSRui Paulo
14125b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
14135b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
14145b9c547cSRui Paulo body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
14155b9c547cSRui Paulo ieee802_1x_mka_dump_sak_use_body(body);
14165b9c547cSRui Paulo
14175b9c547cSRui Paulo if ((body_len != 0) && (body_len < 40)) {
14185b9c547cSRui Paulo wpa_printf(MSG_ERROR,
1419780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets",
1420780fb4a2SCy Schubert body_len);
14215b9c547cSRui Paulo return -1;
14225b9c547cSRui Paulo }
14235b9c547cSRui Paulo
14245b9c547cSRui Paulo /* TODO: what action should I take when peer does not support MACsec */
14255b9c547cSRui Paulo if (body_len == 0) {
14265b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
14275b9c547cSRui Paulo return 0;
14285b9c547cSRui Paulo }
14295b9c547cSRui Paulo
14305b9c547cSRui Paulo /* TODO: when the plain tx or rx of peer is true, should I change
14315b9c547cSRui Paulo * the attribute of controlled port
14325b9c547cSRui Paulo */
14335b9c547cSRui Paulo if (body->prx)
14345b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
14355b9c547cSRui Paulo
14365b9c547cSRui Paulo if (body->ptx)
14375b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
14385b9c547cSRui Paulo /* TODO: how to set the MACsec hardware when delay_protect is true */
1439780fb4a2SCy Schubert if (body->delay_protect &&
1440780fb4a2SCy Schubert (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
14415b9c547cSRui Paulo wpa_printf(MSG_WARNING,
14424bc52338SCy Schubert "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE");
14435b9c547cSRui Paulo return -1;
14445b9c547cSRui Paulo }
14455b9c547cSRui Paulo
1446c1d255d3SCy Schubert olpn = be_to_host32(body->olpn);
1447c1d255d3SCy Schubert llpn = be_to_host32(body->llpn);
1448c1d255d3SCy Schubert
1449c1d255d3SCy Schubert /* Our most recent distributed key should be the first in the list.
1450c1d255d3SCy Schubert * If it doesn't exist then we can't really do anything.
1451c1d255d3SCy Schubert * Be lenient and don't return error here as there are legitimate cases
1452c1d255d3SCy Schubert * where this can happen such as when a new participant joins the CA and
1453c1d255d3SCy Schubert * the first frame it receives can have a SAKuse but not distSAK.
1454c1d255d3SCy Schubert */
1455c1d255d3SCy Schubert sa_key = dl_list_first(&participant->sak_list, struct data_key, list);
1456c1d255d3SCy Schubert if (!sa_key) {
1457c1d255d3SCy Schubert wpa_printf(MSG_INFO,
1458c1d255d3SCy Schubert "KaY: We don't have a latest distributed key - ignore SAK use");
1459c1d255d3SCy Schubert return 0;
1460c1d255d3SCy Schubert }
1461c1d255d3SCy Schubert
1462c1d255d3SCy Schubert /* The peer's most recent key will be the "latest key" if it is present
1463c1d255d3SCy Schubert * otherwise it will be the "old key" if in the RETIRE state.
1464c1d255d3SCy Schubert */
1465c1d255d3SCy Schubert if (body->lrx) {
1466c1d255d3SCy Schubert os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
1467c1d255d3SCy Schubert ki.kn = be_to_host32(body->lkn);
1468c1d255d3SCy Schubert lpn = llpn;
1469c1d255d3SCy Schubert } else {
1470c1d255d3SCy Schubert os_memcpy(ki.mi, body->osrv_mi, sizeof(ki.mi));
1471c1d255d3SCy Schubert ki.kn = be_to_host32(body->okn);
1472c1d255d3SCy Schubert lpn = olpn;
1473c1d255d3SCy Schubert }
1474c1d255d3SCy Schubert
1475c1d255d3SCy Schubert /* If the most recent distributed keys don't agree then someone is out
1476c1d255d3SCy Schubert * of sync. Perhaps non key server hasn't processed the most recent
1477c1d255d3SCy Schubert * distSAK yet and the key server is processing an old packet after it
1478c1d255d3SCy Schubert * has done distSAK. Be lenient and don't return error in this
1479c1d255d3SCy Schubert * particular case; otherwise, the key server will reset its MI and
1480c1d255d3SCy Schubert * cause a traffic disruption which is really undesired for a simple
1481c1d255d3SCy Schubert * timing issue.
1482c1d255d3SCy Schubert */
1483c1d255d3SCy Schubert if (!is_ki_equal(&sa_key->key_identifier, &ki)) {
1484c1d255d3SCy Schubert wpa_printf(MSG_INFO,
1485c1d255d3SCy Schubert "KaY: Distributed keys don't match - ignore SAK use");
1486c1d255d3SCy Schubert return 0;
1487c1d255d3SCy Schubert }
1488c1d255d3SCy Schubert sa_key->next_pn = lpn;
1489c1d255d3SCy Schubert
1490c1d255d3SCy Schubert /* The key server must check that all peers are using the most recent
1491c1d255d3SCy Schubert * distributed key. Non key servers must check if the key server is
1492c1d255d3SCy Schubert * transmitting.
1493c1d255d3SCy Schubert */
1494c1d255d3SCy Schubert if (participant->is_key_server) {
1495c1d255d3SCy Schubert struct ieee802_1x_kay_peer *peer_iter;
1496c1d255d3SCy Schubert bool all_receiving = true;
1497c1d255d3SCy Schubert
1498c1d255d3SCy Schubert /* Distributed keys are equal from above comparison. */
1499c1d255d3SCy Schubert peer->sak_used = true;
1500c1d255d3SCy Schubert
1501c1d255d3SCy Schubert dl_list_for_each(peer_iter, &participant->live_peers,
15025b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
1503c1d255d3SCy Schubert if (!peer_iter->sak_used) {
1504c1d255d3SCy Schubert all_receiving = false;
15055b9c547cSRui Paulo break;
15065b9c547cSRui Paulo }
15075b9c547cSRui Paulo }
15085b9c547cSRui Paulo if (all_receiving) {
1509c1d255d3SCy Schubert participant->to_dist_sak = false;
1510c1d255d3SCy Schubert ieee802_1x_cp_set_allreceiving(kay->cp, true);
1511780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
15125b9c547cSRui Paulo }
1513c1d255d3SCy Schubert } else if (peer->is_key_server) {
1514c1d255d3SCy Schubert if (body->ltx) {
1515c1d255d3SCy Schubert ieee802_1x_cp_set_servertransmitting(kay->cp, true);
1516c1d255d3SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
1517c1d255d3SCy Schubert }
1518c1d255d3SCy Schubert }
15195b9c547cSRui Paulo
1520c1d255d3SCy Schubert /* If I'm key server, and detects peer member PN exhaustion, rekey.
1521c1d255d3SCy Schubert * We only need to check the PN of the most recent distributed key. This
1522c1d255d3SCy Schubert * could be the peer's "latest" or "old" key depending on its current
1523c1d255d3SCy Schubert * state. If both "old" and "latest" keys are present then the "old" key
1524c1d255d3SCy Schubert * has already been exhausted.
1525c1d255d3SCy Schubert */
1526c1d255d3SCy Schubert if (participant->is_key_server && lpn > kay->pn_exhaustion) {
1527c1d255d3SCy Schubert participant->new_sak = true;
15285b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
15295b9c547cSRui Paulo }
15305b9c547cSRui Paulo
1531c1d255d3SCy Schubert /* Get the associated RX SAs of the keys for delay protection since both
1532c1d255d3SCy Schubert * can be in use. Delay protect window (communicated via MKA) is tighter
1533c1d255d3SCy Schubert * than SecY's current replay protect window, so tell SecY the new (and
1534c1d255d3SCy Schubert * higher) lpn.
1535c1d255d3SCy Schubert */
1536c1d255d3SCy Schubert if (body->delay_protect) {
1537c1d255d3SCy Schubert struct receive_sc *rxsc;
1538c1d255d3SCy Schubert struct receive_sa *rxsa;
1539c1d255d3SCy Schubert bool found = false;
1540c1d255d3SCy Schubert
1541c1d255d3SCy Schubert dl_list_for_each(rxsc, &participant->rxsc_list,
1542c1d255d3SCy Schubert struct receive_sc, list) {
1543c1d255d3SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list,
1544c1d255d3SCy Schubert struct receive_sa, list) {
15454bc52338SCy Schubert if (sa_key && rxsa->pkey == sa_key) {
1546c1d255d3SCy Schubert found = true;
15475b9c547cSRui Paulo break;
15485b9c547cSRui Paulo }
15495b9c547cSRui Paulo }
15504bc52338SCy Schubert if (found)
15514bc52338SCy Schubert break;
15524bc52338SCy Schubert }
1553c1d255d3SCy Schubert if (found) {
15544bc52338SCy Schubert secy_get_receive_lowest_pn(participant->kay, rxsa);
15554bc52338SCy Schubert if (lpn > rxsa->lowest_pn) {
15564bc52338SCy Schubert rxsa->lowest_pn = lpn;
1557c1d255d3SCy Schubert secy_set_receive_lowest_pn(participant->kay,
1558c1d255d3SCy Schubert rxsa);
1559c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
1560c1d255d3SCy Schubert "KaY: update dist LPN=0x%x", lpn);
15614bc52338SCy Schubert }
1562c1d255d3SCy Schubert }
1563c1d255d3SCy Schubert
1564c1d255d3SCy Schubert /* FIX: Delay protection for the SA being replaced is not
1565c1d255d3SCy Schubert * implemented. Note that this key will be active for at least
1566c1d255d3SCy Schubert * MKA_SAK_RETIRE_TIME (3 seconds) but could be longer depending
1567c1d255d3SCy Schubert * on how long it takes to get from RECEIVE to TRANSMITTING or
1568c1d255d3SCy Schubert * if going via ABANDON. Delay protection does allow PNs within
1569c1d255d3SCy Schubert * a 2 second window, so getting PN would be a lot of work for
1570c1d255d3SCy Schubert * just 1 second's worth of protection.
1571c1d255d3SCy Schubert */
15725b9c547cSRui Paulo }
15735b9c547cSRui Paulo
15745b9c547cSRui Paulo return 0;
15755b9c547cSRui Paulo }
15765b9c547cSRui Paulo
15775b9c547cSRui Paulo
15785b9c547cSRui Paulo /**
15795b9c547cSRui Paulo * ieee802_1x_mka_dist_sak_body_present
15805b9c547cSRui Paulo */
1581c1d255d3SCy Schubert static bool
ieee802_1x_mka_dist_sak_body_present(struct ieee802_1x_mka_participant * participant)15825b9c547cSRui Paulo ieee802_1x_mka_dist_sak_body_present(
15835b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
15845b9c547cSRui Paulo {
15854bc52338SCy Schubert return participant->is_key_server && participant->to_dist_sak &&
15864bc52338SCy Schubert participant->new_key;
15875b9c547cSRui Paulo }
15885b9c547cSRui Paulo
15895b9c547cSRui Paulo
15905b9c547cSRui Paulo /**
15915b9c547cSRui Paulo * ieee802_1x_kay_get_dist_sak_length
15925b9c547cSRui Paulo */
15935b9c547cSRui Paulo static int
ieee802_1x_mka_get_dist_sak_length(struct ieee802_1x_mka_participant * participant)15945b9c547cSRui Paulo ieee802_1x_mka_get_dist_sak_length(
15955b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
15965b9c547cSRui Paulo {
1597780fb4a2SCy Schubert int length = MKA_HDR_LEN;
1598780fb4a2SCy Schubert unsigned int cs_index = participant->kay->macsec_csindex;
15995b9c547cSRui Paulo
1600780fb4a2SCy Schubert if (participant->advised_desired && cs_index < CS_TABLE_SIZE) {
16015b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_dist_sak_body);
16025b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX)
16035b9c547cSRui Paulo length += CS_ID_LEN;
16045b9c547cSRui Paulo
16055b9c547cSRui Paulo length += cipher_suite_tbl[cs_index].sak_len + 8;
16065b9c547cSRui Paulo }
16075b9c547cSRui Paulo
1608780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length);
16095b9c547cSRui Paulo }
16105b9c547cSRui Paulo
16115b9c547cSRui Paulo
16125b9c547cSRui Paulo /**
16135b9c547cSRui Paulo * ieee802_1x_mka_encode_dist_sak_body -
16145b9c547cSRui Paulo */
16155b9c547cSRui Paulo static int
ieee802_1x_mka_encode_dist_sak_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)16165b9c547cSRui Paulo ieee802_1x_mka_encode_dist_sak_body(
16175b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
16185b9c547cSRui Paulo struct wpabuf *buf)
16195b9c547cSRui Paulo {
16205b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body;
16215b9c547cSRui Paulo struct data_key *sak;
16225b9c547cSRui Paulo unsigned int length;
1623780fb4a2SCy Schubert unsigned int cs_index;
16245b9c547cSRui Paulo int sak_pos;
16255b9c547cSRui Paulo
16265b9c547cSRui Paulo length = ieee802_1x_mka_get_dist_sak_length(participant);
16275b9c547cSRui Paulo body = wpabuf_put(buf, length);
16285b9c547cSRui Paulo body->type = MKA_DISTRIBUTED_SAK;
16295b9c547cSRui Paulo set_mka_param_body_len(body, length - MKA_HDR_LEN);
16305b9c547cSRui Paulo if (length == MKA_HDR_LEN) {
16315b9c547cSRui Paulo body->confid_offset = 0;
16325b9c547cSRui Paulo body->dan = 0;
16335b9c547cSRui Paulo return 0;
16345b9c547cSRui Paulo }
16355b9c547cSRui Paulo
16365b9c547cSRui Paulo sak = participant->new_key;
16374bc52338SCy Schubert if (!sak) {
16384bc52338SCy Schubert wpa_printf(MSG_DEBUG,
16394bc52338SCy Schubert "KaY: No SAK available to build Distributed SAK parameter set");
16404bc52338SCy Schubert return -1;
16414bc52338SCy Schubert }
16425b9c547cSRui Paulo body->confid_offset = sak->confidentiality_offset;
16435b9c547cSRui Paulo body->dan = sak->an;
16445b9c547cSRui Paulo body->kn = host_to_be32(sak->key_identifier.kn);
16455b9c547cSRui Paulo cs_index = participant->kay->macsec_csindex;
16465b9c547cSRui Paulo sak_pos = 0;
1647780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE)
1648780fb4a2SCy Schubert return -1;
16495b9c547cSRui Paulo if (cs_index != DEFAULT_CS_INDEX) {
1650780fb4a2SCy Schubert be64 cs;
1651780fb4a2SCy Schubert
1652780fb4a2SCy Schubert cs = host_to_be64(cipher_suite_tbl[cs_index].id);
1653780fb4a2SCy Schubert os_memcpy(body->sak, &cs, CS_ID_LEN);
16545b9c547cSRui Paulo sak_pos = CS_ID_LEN;
16555b9c547cSRui Paulo }
16564bc52338SCy Schubert if (aes_wrap(participant->kek.key, participant->kek.len,
16575b9c547cSRui Paulo cipher_suite_tbl[cs_index].sak_len / 8,
16585b9c547cSRui Paulo sak->key, body->sak + sak_pos)) {
16595b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
16605b9c547cSRui Paulo return -1;
16615b9c547cSRui Paulo }
16625b9c547cSRui Paulo
16635b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body);
16645b9c547cSRui Paulo
16655b9c547cSRui Paulo return 0;
16665b9c547cSRui Paulo }
16675b9c547cSRui Paulo
16685b9c547cSRui Paulo
16695b9c547cSRui Paulo /**
16705b9c547cSRui Paulo * ieee802_1x_kay_init_data_key -
16715b9c547cSRui Paulo */
ieee802_1x_kay_init_data_key(struct data_key * pkey)1672780fb4a2SCy Schubert static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
16735b9c547cSRui Paulo {
1674c1d255d3SCy Schubert pkey->transmits = true;
1675c1d255d3SCy Schubert pkey->receives = true;
16765b9c547cSRui Paulo os_get_time(&pkey->created_time);
16775b9c547cSRui Paulo
16784bc52338SCy Schubert pkey->next_pn = 1;
16795b9c547cSRui Paulo pkey->user = 1;
16805b9c547cSRui Paulo }
16815b9c547cSRui Paulo
16825b9c547cSRui Paulo
16835b9c547cSRui Paulo /**
16845b9c547cSRui Paulo * ieee802_1x_kay_decode_dist_sak_body -
16855b9c547cSRui Paulo */
16865b9c547cSRui Paulo static int
ieee802_1x_mka_decode_dist_sak_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)16875b9c547cSRui Paulo ieee802_1x_mka_decode_dist_sak_body(
16885b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
16895b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
16905b9c547cSRui Paulo {
16915b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
16925b9c547cSRui Paulo struct ieee802_1x_mka_dist_sak_body *body;
16935b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
16945b9c547cSRui Paulo struct macsec_ciphersuite *cs;
16955b9c547cSRui Paulo size_t body_len;
16965b9c547cSRui Paulo struct data_key *sa_key = NULL;
16975b9c547cSRui Paulo int sak_len;
16985b9c547cSRui Paulo u8 *wrap_sak;
16995b9c547cSRui Paulo u8 *unwrap_sak;
1700780fb4a2SCy Schubert struct ieee802_1x_kay *kay = participant->kay;
17015b9c547cSRui Paulo
17025b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
17035b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
17045b9c547cSRui Paulo if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
17055b9c547cSRui Paulo wpa_printf(MSG_ERROR,
1706780fb4a2SCy Schubert "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets",
1707780fb4a2SCy Schubert body_len);
17085b9c547cSRui Paulo return -1;
17095b9c547cSRui Paulo }
17105b9c547cSRui Paulo
17115b9c547cSRui Paulo if (!participant->principal) {
17125b9c547cSRui Paulo wpa_printf(MSG_ERROR,
17135b9c547cSRui Paulo "KaY: I can't accept the distributed SAK as I am not principal");
17145b9c547cSRui Paulo return -1;
17155b9c547cSRui Paulo }
17165b9c547cSRui Paulo if (participant->is_key_server) {
17175b9c547cSRui Paulo wpa_printf(MSG_ERROR,
17184bc52338SCy Schubert "KaY: Reject distributed SAK since I'm a key server");
17195b9c547cSRui Paulo return -1;
17205b9c547cSRui Paulo }
1721780fb4a2SCy Schubert if (!kay->macsec_desired ||
1722780fb4a2SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
17235b9c547cSRui Paulo wpa_printf(MSG_ERROR,
17245b9c547cSRui Paulo "KaY: I am not MACsec-desired or without MACsec capable");
17255b9c547cSRui Paulo return -1;
17265b9c547cSRui Paulo }
17275b9c547cSRui Paulo
17285b9c547cSRui Paulo peer = ieee802_1x_kay_get_live_peer(participant,
17295b9c547cSRui Paulo participant->current_peer_id.mi);
17305b9c547cSRui Paulo if (!peer) {
17315b9c547cSRui Paulo wpa_printf(MSG_ERROR,
17325b9c547cSRui Paulo "KaY: The key server is not in my live peers list");
17335b9c547cSRui Paulo return -1;
17345b9c547cSRui Paulo }
1735780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &peer->sci)) {
17365b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
17375b9c547cSRui Paulo return -1;
17385b9c547cSRui Paulo }
1739780fb4a2SCy Schubert
17405b9c547cSRui Paulo if (body_len == 0) {
1741c1d255d3SCy Schubert kay->authenticated = true;
1742c1d255d3SCy Schubert kay->secured = false;
1743c1d255d3SCy Schubert kay->failed = false;
1744c1d255d3SCy Schubert participant->advised_desired = false;
1745780fb4a2SCy Schubert ieee802_1x_cp_connect_authenticated(kay->cp);
1746780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
17475b9c547cSRui Paulo wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec");
1748c1d255d3SCy Schubert participant->to_use_sak = false;
17495b9c547cSRui Paulo return 0;
17505b9c547cSRui Paulo }
1751780fb4a2SCy Schubert
1752c1d255d3SCy Schubert participant->advised_desired = true;
1753c1d255d3SCy Schubert kay->authenticated = false;
1754c1d255d3SCy Schubert kay->secured = true;
1755c1d255d3SCy Schubert kay->failed = false;
1756780fb4a2SCy Schubert ieee802_1x_cp_connect_secure(kay->cp);
1757780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
17585b9c547cSRui Paulo
17595b9c547cSRui Paulo body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
17605b9c547cSRui Paulo ieee802_1x_mka_dump_dist_sak_body(body);
17615b9c547cSRui Paulo dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
17625b9c547cSRui Paulo {
17635b9c547cSRui Paulo if (os_memcmp(sa_key->key_identifier.mi,
17645b9c547cSRui Paulo participant->current_peer_id.mi, MI_LEN) == 0 &&
17655b9c547cSRui Paulo sa_key->key_identifier.kn == be_to_host32(body->kn)) {
17664bc52338SCy Schubert wpa_printf(MSG_DEBUG,
17674bc52338SCy Schubert "KaY: SAK has already been installed - do not set it again");
17685b9c547cSRui Paulo return 0;
17695b9c547cSRui Paulo }
17705b9c547cSRui Paulo }
1771780fb4a2SCy Schubert
17725b9c547cSRui Paulo if (body_len == 28) {
17735b9c547cSRui Paulo sak_len = DEFAULT_SA_KEY_LEN;
17745b9c547cSRui Paulo wrap_sak = body->sak;
1775780fb4a2SCy Schubert kay->macsec_csindex = DEFAULT_CS_INDEX;
1776780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex];
17775b9c547cSRui Paulo } else {
17784bc52338SCy Schubert unsigned int idx;
17794bc52338SCy Schubert
17804bc52338SCy Schubert cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak,
17814bc52338SCy Schubert &idx);
17825b9c547cSRui Paulo if (!cs) {
17835b9c547cSRui Paulo wpa_printf(MSG_ERROR,
17845b9c547cSRui Paulo "KaY: I can't support the Cipher Suite advised by key server");
17855b9c547cSRui Paulo return -1;
17865b9c547cSRui Paulo }
17875b9c547cSRui Paulo sak_len = cs->sak_len;
17885b9c547cSRui Paulo wrap_sak = body->sak + CS_ID_LEN;
17894bc52338SCy Schubert kay->macsec_csindex = idx;
17905b9c547cSRui Paulo }
17915b9c547cSRui Paulo
17925b9c547cSRui Paulo unwrap_sak = os_zalloc(sak_len);
17935b9c547cSRui Paulo if (!unwrap_sak) {
17945b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
17955b9c547cSRui Paulo return -1;
17965b9c547cSRui Paulo }
17974bc52338SCy Schubert if (aes_unwrap(participant->kek.key, participant->kek.len,
17984bc52338SCy Schubert sak_len >> 3, wrap_sak, unwrap_sak)) {
17995b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
18005b9c547cSRui Paulo os_free(unwrap_sak);
18015b9c547cSRui Paulo return -1;
18025b9c547cSRui Paulo }
18034bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:",
180485732ac8SCy Schubert unwrap_sak, sak_len);
18055b9c547cSRui Paulo
1806780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key));
18075b9c547cSRui Paulo if (!sa_key) {
18085b9c547cSRui Paulo os_free(unwrap_sak);
18095b9c547cSRui Paulo return -1;
18105b9c547cSRui Paulo }
18115b9c547cSRui Paulo
1812780fb4a2SCy Schubert os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi,
1813780fb4a2SCy Schubert MI_LEN);
1814780fb4a2SCy Schubert sa_key->key_identifier.kn = be_to_host32(body->kn);
1815780fb4a2SCy Schubert
1816780fb4a2SCy Schubert sa_key->key = unwrap_sak;
1817780fb4a2SCy Schubert sa_key->key_len = sak_len;
1818780fb4a2SCy Schubert
1819780fb4a2SCy Schubert sa_key->confidentiality_offset = body->confid_offset;
1820780fb4a2SCy Schubert sa_key->an = body->dan;
1821780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key);
1822780fb4a2SCy Schubert
182385732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key);
18245b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list);
18255b9c547cSRui Paulo
1826780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
1827780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
1828780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, body->confid_offset);
1829780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
1830780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
1831780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, body->dan);
1832780fb4a2SCy Schubert ieee802_1x_cp_signal_newsak(kay->cp);
1833780fb4a2SCy Schubert ieee802_1x_cp_sm_step(kay->cp);
18345b9c547cSRui Paulo
183585732ac8SCy Schubert kay->rcvd_keys++;
1836c1d255d3SCy Schubert participant->to_use_sak = true;
18375b9c547cSRui Paulo
1838*a90b9d01SCy Schubert /*
1839*a90b9d01SCy Schubert * The key server may not include dist sak and use sak in one packet.
1840*a90b9d01SCy Schubert * Meanwhile, after dist sak, the current participant (non-key server)
1841*a90b9d01SCy Schubert * will install SC or SA(s) after decoding the dist sak which may take
1842*a90b9d01SCy Schubert * few seconds in real physical platforms. Meanwhile, the peer expire
1843*a90b9d01SCy Schubert * time is always initialized at adding the key server to peer list.
1844*a90b9d01SCy Schubert * The gap between adding the key server to peer list and processing
1845*a90b9d01SCy Schubert * next use sak packet may exceed the threshold of MKA_LIFE_TIME (6 s).
1846*a90b9d01SCy Schubert * It will cause an unexpected cleanup (delete SC and SA(s)), so,
1847*a90b9d01SCy Schubert * update the expire timeout at dist sak also. */
1848*a90b9d01SCy Schubert peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
1849*a90b9d01SCy Schubert
18505b9c547cSRui Paulo return 0;
18515b9c547cSRui Paulo }
18525b9c547cSRui Paulo
18535b9c547cSRui Paulo
18545b9c547cSRui Paulo /**
18555b9c547cSRui Paulo * ieee802_1x_mka_icv_body_present
18565b9c547cSRui Paulo */
1857c1d255d3SCy Schubert static bool
ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant * participant)18585b9c547cSRui Paulo ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
18595b9c547cSRui Paulo {
1860c1d255d3SCy Schubert return true;
18615b9c547cSRui Paulo }
18625b9c547cSRui Paulo
18635b9c547cSRui Paulo
18645b9c547cSRui Paulo /**
18655b9c547cSRui Paulo * ieee802_1x_kay_get_icv_length
18665b9c547cSRui Paulo */
18675b9c547cSRui Paulo static int
ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant * participant)18685b9c547cSRui Paulo ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
18695b9c547cSRui Paulo {
18705b9c547cSRui Paulo int length;
18715b9c547cSRui Paulo
18724bc52338SCy Schubert /* Determine if we need space for the ICV Indicator */
18734bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
18744bc52338SCy Schubert DEFAULT_ICV_LEN)
18755b9c547cSRui Paulo length = sizeof(struct ieee802_1x_mka_icv_body);
18764bc52338SCy Schubert else
18774bc52338SCy Schubert length = 0;
18785b9c547cSRui Paulo length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
18795b9c547cSRui Paulo
1880780fb4a2SCy Schubert return MKA_ALIGN_LENGTH(length);
18815b9c547cSRui Paulo }
18825b9c547cSRui Paulo
18835b9c547cSRui Paulo
18845b9c547cSRui Paulo /**
18855b9c547cSRui Paulo * ieee802_1x_mka_encode_icv_body -
18865b9c547cSRui Paulo */
18875b9c547cSRui Paulo static int
ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant * participant,struct wpabuf * buf)18885b9c547cSRui Paulo ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
18895b9c547cSRui Paulo struct wpabuf *buf)
18905b9c547cSRui Paulo {
18915b9c547cSRui Paulo struct ieee802_1x_mka_icv_body *body;
18925b9c547cSRui Paulo unsigned int length;
18935b9c547cSRui Paulo u8 cmac[MAX_ICV_LEN];
18945b9c547cSRui Paulo
18955b9c547cSRui Paulo length = ieee802_1x_mka_get_icv_length(participant);
18964bc52338SCy Schubert if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
18974bc52338SCy Schubert DEFAULT_ICV_LEN) {
18984bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
18995b9c547cSRui Paulo body = wpabuf_put(buf, MKA_HDR_LEN);
19005b9c547cSRui Paulo body->type = MKA_ICV_INDICATOR;
19014bc52338SCy Schubert length -= MKA_HDR_LEN;
19024bc52338SCy Schubert set_mka_param_body_len(body, length);
19035b9c547cSRui Paulo }
19045b9c547cSRui Paulo
19055b9c547cSRui Paulo if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
19064bc52338SCy Schubert participant->ick.key, participant->ick.len,
19074bc52338SCy Schubert wpabuf_head(buf), wpabuf_len(buf), cmac)) {
19084bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV");
19095b9c547cSRui Paulo return -1;
19105b9c547cSRui Paulo }
19114bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length);
19125b9c547cSRui Paulo
19135b9c547cSRui Paulo os_memcpy(wpabuf_put(buf, length), cmac, length);
19145b9c547cSRui Paulo
19155b9c547cSRui Paulo return 0;
19165b9c547cSRui Paulo }
19175b9c547cSRui Paulo
19185b9c547cSRui Paulo /**
19195b9c547cSRui Paulo * ieee802_1x_mka_decode_icv_body -
19205b9c547cSRui Paulo */
19214bc52338SCy Schubert static const u8 *
ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)19225b9c547cSRui Paulo ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
19235b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
19245b9c547cSRui Paulo {
19254bc52338SCy Schubert const struct ieee802_1x_mka_hdr *hdr;
19264bc52338SCy Schubert const struct ieee802_1x_mka_icv_body *body;
19275b9c547cSRui Paulo size_t body_len;
19285b9c547cSRui Paulo size_t left_len;
1929780fb4a2SCy Schubert u8 body_type;
19305b9c547cSRui Paulo const u8 *pos;
19315b9c547cSRui Paulo
19325b9c547cSRui Paulo pos = mka_msg;
19335b9c547cSRui Paulo left_len = msg_len;
19344bc52338SCy Schubert while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
19354bc52338SCy Schubert hdr = (const struct ieee802_1x_mka_hdr *) pos;
19364bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
19375b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr);
19385b9c547cSRui Paulo
19394bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN)
19405b9c547cSRui Paulo break;
19415b9c547cSRui Paulo
19425b9c547cSRui Paulo if (body_type != MKA_ICV_INDICATOR) {
19435b9c547cSRui Paulo left_len -= MKA_HDR_LEN + body_len;
19445b9c547cSRui Paulo pos += MKA_HDR_LEN + body_len;
19455b9c547cSRui Paulo continue;
19465b9c547cSRui Paulo }
19475b9c547cSRui Paulo
19484bc52338SCy Schubert body = (const struct ieee802_1x_mka_icv_body *) pos;
19495b9c547cSRui Paulo if (body_len
19504bc52338SCy Schubert < mka_alg_tbl[participant->kay->mka_algindex].icv_len)
19515b9c547cSRui Paulo return NULL;
19525b9c547cSRui Paulo
19535b9c547cSRui Paulo return body->icv;
19545b9c547cSRui Paulo }
19555b9c547cSRui Paulo
19564bc52338SCy Schubert return mka_msg + msg_len - DEFAULT_ICV_LEN;
19575b9c547cSRui Paulo }
19585b9c547cSRui Paulo
19595b9c547cSRui Paulo
19605b9c547cSRui Paulo /**
19615b9c547cSRui Paulo * ieee802_1x_mka_decode_dist_cak_body-
19625b9c547cSRui Paulo */
19635b9c547cSRui Paulo static int
ieee802_1x_mka_decode_dist_cak_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)19645b9c547cSRui Paulo ieee802_1x_mka_decode_dist_cak_body(
19655b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
19665b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
19675b9c547cSRui Paulo {
19685b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
19695b9c547cSRui Paulo size_t body_len;
19705b9c547cSRui Paulo
19715b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19725b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
19735b9c547cSRui Paulo if (body_len < 28) {
19745b9c547cSRui Paulo wpa_printf(MSG_ERROR,
19754bc52338SCy Schubert "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets",
1976780fb4a2SCy Schubert body_len);
19775b9c547cSRui Paulo return -1;
19785b9c547cSRui Paulo }
19795b9c547cSRui Paulo
19805b9c547cSRui Paulo return 0;
19815b9c547cSRui Paulo }
19825b9c547cSRui Paulo
19835b9c547cSRui Paulo
19845b9c547cSRui Paulo /**
19855b9c547cSRui Paulo * ieee802_1x_mka_decode_kmd_body -
19865b9c547cSRui Paulo */
19875b9c547cSRui Paulo static int
ieee802_1x_mka_decode_kmd_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)19885b9c547cSRui Paulo ieee802_1x_mka_decode_kmd_body(
19895b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
19905b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
19915b9c547cSRui Paulo {
19925b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
19935b9c547cSRui Paulo size_t body_len;
19945b9c547cSRui Paulo
19955b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
19965b9c547cSRui Paulo body_len = get_mka_param_body_len(hdr);
19975b9c547cSRui Paulo if (body_len < 5) {
19985b9c547cSRui Paulo wpa_printf(MSG_ERROR,
19994bc52338SCy Schubert "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets",
2000780fb4a2SCy Schubert body_len);
20015b9c547cSRui Paulo return -1;
20025b9c547cSRui Paulo }
20035b9c547cSRui Paulo
20045b9c547cSRui Paulo return 0;
20055b9c547cSRui Paulo }
20065b9c547cSRui Paulo
20075b9c547cSRui Paulo
20085b9c547cSRui Paulo /**
20095b9c547cSRui Paulo * ieee802_1x_mka_decode_announce_body -
20105b9c547cSRui Paulo */
ieee802_1x_mka_decode_announce_body(struct ieee802_1x_mka_participant * participant,const u8 * mka_msg,size_t msg_len)20115b9c547cSRui Paulo static int ieee802_1x_mka_decode_announce_body(
20125b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant,
20135b9c547cSRui Paulo const u8 *mka_msg, size_t msg_len)
20145b9c547cSRui Paulo {
20155b9c547cSRui Paulo return 0;
20165b9c547cSRui Paulo }
20175b9c547cSRui Paulo
20185b9c547cSRui Paulo
2019780fb4a2SCy Schubert struct mka_param_body_handler {
2020780fb4a2SCy Schubert int (*body_tx)(struct ieee802_1x_mka_participant *participant,
2021780fb4a2SCy Schubert struct wpabuf *buf);
2022780fb4a2SCy Schubert int (*body_rx)(struct ieee802_1x_mka_participant *participant,
2023780fb4a2SCy Schubert const u8 *mka_msg, size_t msg_len);
2024780fb4a2SCy Schubert int (*body_length)(struct ieee802_1x_mka_participant *participant);
2025c1d255d3SCy Schubert bool (*body_present)(struct ieee802_1x_mka_participant *participant);
2026780fb4a2SCy Schubert };
2027780fb4a2SCy Schubert
2028780fb4a2SCy Schubert
2029780fb4a2SCy Schubert static struct mka_param_body_handler mka_body_handler[] = {
20304bc52338SCy Schubert /* Basic parameter set */
20315b9c547cSRui Paulo {
2032780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_basic_body,
2033780fb4a2SCy Schubert .body_rx = NULL,
2034780fb4a2SCy Schubert .body_length = ieee802_1x_mka_basic_body_length,
2035780fb4a2SCy Schubert .body_present = ieee802_1x_mka_basic_body_present
20365b9c547cSRui Paulo },
20375b9c547cSRui Paulo
20384bc52338SCy Schubert /* Live Peer List parameter set */
20395b9c547cSRui Paulo {
2040780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_live_peer_body,
2041780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_live_peer_body,
2042780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_live_peer_length,
2043780fb4a2SCy Schubert .body_present = ieee802_1x_mka_live_peer_body_present
20445b9c547cSRui Paulo },
20455b9c547cSRui Paulo
20464bc52338SCy Schubert /* Potential Peer List parameter set */
20475b9c547cSRui Paulo {
2048780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_potential_peer_body,
2049780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_potential_peer_body,
2050780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_potential_peer_length,
2051780fb4a2SCy Schubert .body_present = ieee802_1x_mka_potential_peer_body_present
20525b9c547cSRui Paulo },
20535b9c547cSRui Paulo
20544bc52338SCy Schubert /* MACsec SAK Use parameter set */
20555b9c547cSRui Paulo {
2056780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_sak_use_body,
2057780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_sak_use_body,
2058780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_sak_use_length,
2059780fb4a2SCy Schubert .body_present = ieee802_1x_mka_sak_use_body_present
20605b9c547cSRui Paulo },
20615b9c547cSRui Paulo
20624bc52338SCy Schubert /* Distributed SAK parameter set */
20635b9c547cSRui Paulo {
2064780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_dist_sak_body,
2065780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_sak_body,
2066780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_dist_sak_length,
2067780fb4a2SCy Schubert .body_present = ieee802_1x_mka_dist_sak_body_present
20685b9c547cSRui Paulo },
20695b9c547cSRui Paulo
20704bc52338SCy Schubert /* Distribute CAK parameter set */
20715b9c547cSRui Paulo {
2072780fb4a2SCy Schubert .body_tx = NULL,
2073780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_dist_cak_body,
2074780fb4a2SCy Schubert .body_length = NULL,
2075780fb4a2SCy Schubert .body_present = NULL
20765b9c547cSRui Paulo },
20775b9c547cSRui Paulo
20784bc52338SCy Schubert /* KMD parameter set */
20795b9c547cSRui Paulo {
2080780fb4a2SCy Schubert .body_tx = NULL,
2081780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_kmd_body,
2082780fb4a2SCy Schubert .body_length = NULL,
2083780fb4a2SCy Schubert .body_present = NULL
20845b9c547cSRui Paulo },
20855b9c547cSRui Paulo
20864bc52338SCy Schubert /* Announcement parameter set */
20875b9c547cSRui Paulo {
2088780fb4a2SCy Schubert .body_tx = NULL,
2089780fb4a2SCy Schubert .body_rx = ieee802_1x_mka_decode_announce_body,
2090780fb4a2SCy Schubert .body_length = NULL,
2091780fb4a2SCy Schubert .body_present = NULL
20925b9c547cSRui Paulo },
20935b9c547cSRui Paulo
20944bc52338SCy Schubert /* ICV Indicator parameter set */
20955b9c547cSRui Paulo {
2096780fb4a2SCy Schubert .body_tx = ieee802_1x_mka_encode_icv_body,
2097780fb4a2SCy Schubert .body_rx = NULL,
2098780fb4a2SCy Schubert .body_length = ieee802_1x_mka_get_icv_length,
2099780fb4a2SCy Schubert .body_present = ieee802_1x_mka_icv_body_present
21005b9c547cSRui Paulo },
21015b9c547cSRui Paulo };
21025b9c547cSRui Paulo
21035b9c547cSRui Paulo
21045b9c547cSRui Paulo /**
210585732ac8SCy Schubert * ieee802_1x_kay_use_data_key - Take reference on a key
210685732ac8SCy Schubert */
ieee802_1x_kay_use_data_key(struct data_key * pkey)210785732ac8SCy Schubert static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
210885732ac8SCy Schubert {
210985732ac8SCy Schubert pkey->user++;
211085732ac8SCy Schubert }
211185732ac8SCy Schubert
211285732ac8SCy Schubert
211385732ac8SCy Schubert /**
211485732ac8SCy Schubert * ieee802_1x_kay_deinit_data_key - Release reference on a key and
211585732ac8SCy Schubert * free if there are no remaining users
21165b9c547cSRui Paulo */
ieee802_1x_kay_deinit_data_key(struct data_key * pkey)2117780fb4a2SCy Schubert static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
21185b9c547cSRui Paulo {
21195b9c547cSRui Paulo if (!pkey)
21205b9c547cSRui Paulo return;
21215b9c547cSRui Paulo
21225b9c547cSRui Paulo pkey->user--;
21235b9c547cSRui Paulo if (pkey->user > 1)
21245b9c547cSRui Paulo return;
21255b9c547cSRui Paulo
21265b9c547cSRui Paulo os_free(pkey->key);
21275b9c547cSRui Paulo os_free(pkey);
21285b9c547cSRui Paulo }
21295b9c547cSRui Paulo
21305b9c547cSRui Paulo
21315b9c547cSRui Paulo /**
21325b9c547cSRui Paulo * ieee802_1x_kay_generate_new_sak -
21335b9c547cSRui Paulo */
21345b9c547cSRui Paulo static int
ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant * participant)21355b9c547cSRui Paulo ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
21365b9c547cSRui Paulo {
21375b9c547cSRui Paulo struct data_key *sa_key = NULL;
21385b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
21395b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay;
21405b9c547cSRui Paulo int ctx_len, ctx_offset;
21415b9c547cSRui Paulo u8 *context;
2142780fb4a2SCy Schubert unsigned int key_len;
2143780fb4a2SCy Schubert u8 *key;
2144780fb4a2SCy Schubert struct macsec_ciphersuite *cs;
21455b9c547cSRui Paulo
21465b9c547cSRui Paulo /* check condition for generating a fresh SAK:
21475b9c547cSRui Paulo * must have one live peer
21485b9c547cSRui Paulo * and MKA life time elapse since last distribution
21495b9c547cSRui Paulo * or potential peer is empty
21505b9c547cSRui Paulo */
21515b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) {
21525b9c547cSRui Paulo wpa_printf(MSG_ERROR,
21534bc52338SCy Schubert "KaY: Live peers list must not be empty when generating fresh SAK");
21545b9c547cSRui Paulo return -1;
21555b9c547cSRui Paulo }
21565b9c547cSRui Paulo
21575b9c547cSRui Paulo /* FIXME: A fresh SAK not generated until
21585b9c547cSRui Paulo * the live peer list contains at least one peer and
21595b9c547cSRui Paulo * MKA life time has elapsed since the prior SAK was first distributed,
21605b9c547cSRui Paulo * or the Key server's potential peer is empty
21615b9c547cSRui Paulo * but I can't understand the second item, so
21625b9c547cSRui Paulo * here only check first item and ingore
21635b9c547cSRui Paulo * && (!dl_list_empty(&participant->potential_peers))) {
21645b9c547cSRui Paulo */
21655b9c547cSRui Paulo if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
21665b9c547cSRui Paulo wpa_printf(MSG_ERROR,
21674bc52338SCy Schubert "KaY: Life time has not elapsed since prior SAK distributed");
21685b9c547cSRui Paulo return -1;
21695b9c547cSRui Paulo }
21705b9c547cSRui Paulo
2171780fb4a2SCy Schubert cs = &cipher_suite_tbl[kay->macsec_csindex];
2172780fb4a2SCy Schubert key_len = cs->sak_len;
2173780fb4a2SCy Schubert key = os_zalloc(key_len);
2174780fb4a2SCy Schubert if (!key) {
21755b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
21765b9c547cSRui Paulo return -1;
21775b9c547cSRui Paulo }
21785b9c547cSRui Paulo
2179780fb4a2SCy Schubert ctx_len = key_len + sizeof(kay->dist_kn);
21805b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
21815b9c547cSRui Paulo struct ieee802_1x_kay_peer, list)
21825b9c547cSRui Paulo ctx_len += sizeof(peer->mi);
21835b9c547cSRui Paulo ctx_len += sizeof(participant->mi);
21845b9c547cSRui Paulo
21855b9c547cSRui Paulo context = os_zalloc(ctx_len);
2186780fb4a2SCy Schubert if (!context)
2187780fb4a2SCy Schubert goto fail;
2188780fb4a2SCy Schubert
21895b9c547cSRui Paulo ctx_offset = 0;
2190780fb4a2SCy Schubert if (os_get_random(context + ctx_offset, key_len) < 0)
2191780fb4a2SCy Schubert goto fail;
2192780fb4a2SCy Schubert
2193780fb4a2SCy Schubert ctx_offset += key_len;
21945b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
21955b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
21965b9c547cSRui Paulo os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
21975b9c547cSRui Paulo ctx_offset += sizeof(peer->mi);
21985b9c547cSRui Paulo }
21995b9c547cSRui Paulo os_memcpy(context + ctx_offset, participant->mi,
22005b9c547cSRui Paulo sizeof(participant->mi));
22015b9c547cSRui Paulo ctx_offset += sizeof(participant->mi);
22025b9c547cSRui Paulo os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
22035b9c547cSRui Paulo
22044bc52338SCy Schubert if (key_len == 16 || key_len == 32) {
22054bc52338SCy Schubert if (ieee802_1x_sak_aes_cmac(participant->cak.key,
22064bc52338SCy Schubert participant->cak.len,
22074bc52338SCy Schubert context, ctx_len,
22084bc52338SCy Schubert key, key_len)) {
22094bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK");
22104bc52338SCy Schubert goto fail;
22114bc52338SCy Schubert }
22125b9c547cSRui Paulo } else {
22134bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported",
22144bc52338SCy Schubert key_len);
2215780fb4a2SCy Schubert goto fail;
22165b9c547cSRui Paulo }
221785732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
2218780fb4a2SCy Schubert os_free(context);
2219780fb4a2SCy Schubert context = NULL;
22205b9c547cSRui Paulo
2221780fb4a2SCy Schubert sa_key = os_zalloc(sizeof(*sa_key));
22225b9c547cSRui Paulo if (!sa_key) {
2223780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
2224780fb4a2SCy Schubert goto fail;
22255b9c547cSRui Paulo }
2226780fb4a2SCy Schubert
2227780fb4a2SCy Schubert sa_key->key = key;
2228780fb4a2SCy Schubert sa_key->key_len = key_len;
2229780fb4a2SCy Schubert os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN);
2230780fb4a2SCy Schubert sa_key->key_identifier.kn = kay->dist_kn;
2231780fb4a2SCy Schubert
2232780fb4a2SCy Schubert sa_key->confidentiality_offset = kay->macsec_confidentiality;
2233780fb4a2SCy Schubert sa_key->an = kay->dist_an;
2234780fb4a2SCy Schubert ieee802_1x_kay_init_data_key(sa_key);
2235780fb4a2SCy Schubert
22365b9c547cSRui Paulo participant->new_key = sa_key;
22375b9c547cSRui Paulo
223885732ac8SCy Schubert ieee802_1x_kay_use_data_key(sa_key);
22395b9c547cSRui Paulo dl_list_add(&participant->sak_list, &sa_key->list);
224085732ac8SCy Schubert
2241780fb4a2SCy Schubert ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
22425b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
2243780fb4a2SCy Schubert ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
22445b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
2245780fb4a2SCy Schubert ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
2246780fb4a2SCy Schubert ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an);
22475b9c547cSRui Paulo ieee802_1x_cp_signal_newsak(kay->cp);
22485b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
22495b9c547cSRui Paulo
22505b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
22515b9c547cSRui Paulo struct ieee802_1x_kay_peer, list)
2252c1d255d3SCy Schubert peer->sak_used = false;
22535b9c547cSRui Paulo
2254780fb4a2SCy Schubert kay->dist_kn++;
2255780fb4a2SCy Schubert kay->dist_an++;
2256780fb4a2SCy Schubert if (kay->dist_an > 3)
2257780fb4a2SCy Schubert kay->dist_an = 0;
22585b9c547cSRui Paulo
2259780fb4a2SCy Schubert kay->dist_time = time(NULL);
22605b9c547cSRui Paulo
22615b9c547cSRui Paulo return 0;
2262780fb4a2SCy Schubert
2263780fb4a2SCy Schubert fail:
2264780fb4a2SCy Schubert os_free(key);
2265780fb4a2SCy Schubert os_free(context);
2266780fb4a2SCy Schubert return -1;
2267780fb4a2SCy Schubert }
2268780fb4a2SCy Schubert
2269780fb4a2SCy Schubert
compare_priorities(const struct ieee802_1x_kay_peer * peer,const struct ieee802_1x_kay_peer * other)2270780fb4a2SCy Schubert static int compare_priorities(const struct ieee802_1x_kay_peer *peer,
2271780fb4a2SCy Schubert const struct ieee802_1x_kay_peer *other)
2272780fb4a2SCy Schubert {
2273780fb4a2SCy Schubert if (peer->key_server_priority < other->key_server_priority)
2274780fb4a2SCy Schubert return -1;
2275780fb4a2SCy Schubert if (other->key_server_priority < peer->key_server_priority)
2276780fb4a2SCy Schubert return 1;
2277780fb4a2SCy Schubert
2278780fb4a2SCy Schubert return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN);
22795b9c547cSRui Paulo }
22805b9c547cSRui Paulo
22815b9c547cSRui Paulo
22825b9c547cSRui Paulo /**
22835b9c547cSRui Paulo * ieee802_1x_kay_elect_key_server - elect the key server
22845b9c547cSRui Paulo * when to elect: whenever the live peers list changes
22855b9c547cSRui Paulo */
22865b9c547cSRui Paulo static int
ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant * participant)22875b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
22885b9c547cSRui Paulo {
22895b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
22905b9c547cSRui Paulo struct ieee802_1x_kay_peer *key_server = NULL;
22915b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay;
2292c1d255d3SCy Schubert bool i_is_key_server;
229385732ac8SCy Schubert int priority_comparison;
22945b9c547cSRui Paulo
22955b9c547cSRui Paulo if (participant->is_obliged_key_server) {
2296c1d255d3SCy Schubert participant->new_sak = true;
2297c1d255d3SCy Schubert participant->to_dist_sak = false;
2298c1d255d3SCy Schubert ieee802_1x_cp_set_electedself(kay->cp, true);
22995b9c547cSRui Paulo return 0;
23005b9c547cSRui Paulo }
23015b9c547cSRui Paulo
23025b9c547cSRui Paulo /* elect the key server among the peers */
23035b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
23045b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
23055b9c547cSRui Paulo if (!peer->is_key_server)
23065b9c547cSRui Paulo continue;
23075b9c547cSRui Paulo
23085b9c547cSRui Paulo if (!key_server) {
23095b9c547cSRui Paulo key_server = peer;
23105b9c547cSRui Paulo continue;
23115b9c547cSRui Paulo }
23125b9c547cSRui Paulo
2313780fb4a2SCy Schubert if (compare_priorities(peer, key_server) < 0)
23145b9c547cSRui Paulo key_server = peer;
23155b9c547cSRui Paulo }
23165b9c547cSRui Paulo
23175b9c547cSRui Paulo /* elect the key server between me and the above elected peer */
2318c1d255d3SCy Schubert i_is_key_server = false;
23195b9c547cSRui Paulo if (key_server && participant->can_be_key_server) {
2320780fb4a2SCy Schubert struct ieee802_1x_kay_peer tmp;
23215b9c547cSRui Paulo
2322780fb4a2SCy Schubert tmp.key_server_priority = kay->actor_priority;
2323780fb4a2SCy Schubert os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
232485732ac8SCy Schubert priority_comparison = compare_priorities(&tmp, key_server);
232585732ac8SCy Schubert if (priority_comparison < 0) {
2326c1d255d3SCy Schubert i_is_key_server = true;
232785732ac8SCy Schubert } else if (priority_comparison == 0) {
232885732ac8SCy Schubert wpa_printf(MSG_WARNING,
232985732ac8SCy Schubert "KaY: Cannot elect key server between me and peer, duplicate MAC detected");
233085732ac8SCy Schubert key_server = NULL;
233185732ac8SCy Schubert }
2332780fb4a2SCy Schubert } else if (participant->can_be_key_server) {
2333c1d255d3SCy Schubert i_is_key_server = true;
23345b9c547cSRui Paulo }
23355b9c547cSRui Paulo
23365b9c547cSRui Paulo if (i_is_key_server) {
2337c1d255d3SCy Schubert ieee802_1x_cp_set_electedself(kay->cp, true);
2338780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) {
23395b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp);
23405b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
23415b9c547cSRui Paulo }
23425b9c547cSRui Paulo
2343c1d255d3SCy Schubert participant->is_key_server = true;
2344c1d255d3SCy Schubert participant->principal = true;
2345c1d255d3SCy Schubert participant->new_sak = true;
23464bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: I am elected as key server");
2347c1d255d3SCy Schubert participant->to_dist_sak = false;
2348c1d255d3SCy Schubert participant->is_elected = true;
23495b9c547cSRui Paulo
23505b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci,
23515b9c547cSRui Paulo sizeof(kay->key_server_sci));
23525b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority;
2353780fb4a2SCy Schubert } else if (key_server) {
23544bc52338SCy Schubert wpa_printf(MSG_DEBUG,
23554bc52338SCy Schubert "KaY: Peer %s was elected as the key server",
23564bc52338SCy Schubert mi_txt(key_server->mi));
2357c1d255d3SCy Schubert ieee802_1x_cp_set_electedself(kay->cp, false);
2358780fb4a2SCy Schubert if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
23595b9c547cSRui Paulo ieee802_1x_cp_signal_chgdserver(kay->cp);
23605b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
23615b9c547cSRui Paulo }
23625b9c547cSRui Paulo
2363c1d255d3SCy Schubert participant->is_key_server = false;
2364c1d255d3SCy Schubert participant->principal = true;
2365c1d255d3SCy Schubert participant->is_elected = true;
23665b9c547cSRui Paulo
23675b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &key_server->sci,
23685b9c547cSRui Paulo sizeof(kay->key_server_sci));
23695b9c547cSRui Paulo kay->key_server_priority = key_server->key_server_priority;
2370780fb4a2SCy Schubert } else {
2371c1d255d3SCy Schubert participant->principal = false;
2372c1d255d3SCy Schubert participant->is_key_server = false;
2373c1d255d3SCy Schubert participant->is_elected = false;
23745b9c547cSRui Paulo }
23755b9c547cSRui Paulo
23765b9c547cSRui Paulo return 0;
23775b9c547cSRui Paulo }
23785b9c547cSRui Paulo
23795b9c547cSRui Paulo
23805b9c547cSRui Paulo /**
23815b9c547cSRui Paulo * ieee802_1x_kay_decide_macsec_use - the key server determinate
23825b9c547cSRui Paulo * how to use MACsec: whether use MACsec and its capability
23835b9c547cSRui Paulo * protectFrames will be advised if the key server and one of its live peers are
23845b9c547cSRui Paulo * MACsec capable and one of those request MACsec protection
23855b9c547cSRui Paulo */
23865b9c547cSRui Paulo static int
ieee802_1x_kay_decide_macsec_use(struct ieee802_1x_mka_participant * participant)23875b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(
23885b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
23895b9c547cSRui Paulo {
23905b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay;
23915b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
23925b9c547cSRui Paulo enum macsec_cap less_capability;
2393c1d255d3SCy Schubert bool has_peer;
23945b9c547cSRui Paulo
23955b9c547cSRui Paulo if (!participant->is_key_server)
23965b9c547cSRui Paulo return -1;
23975b9c547cSRui Paulo
23985b9c547cSRui Paulo /* key server self is MACsec-desired and requesting MACsec */
23995b9c547cSRui Paulo if (!kay->macsec_desired) {
2400c1d255d3SCy Schubert participant->advised_desired = false;
24015b9c547cSRui Paulo return -1;
24025b9c547cSRui Paulo }
24035b9c547cSRui Paulo if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
2404c1d255d3SCy Schubert participant->advised_desired = false;
24055b9c547cSRui Paulo return -1;
24065b9c547cSRui Paulo }
24075b9c547cSRui Paulo less_capability = kay->macsec_capable;
24085b9c547cSRui Paulo
24095b9c547cSRui Paulo /* at least one of peers is MACsec-desired and requesting MACsec */
2410c1d255d3SCy Schubert has_peer = false;
24115b9c547cSRui Paulo dl_list_for_each(peer, &participant->live_peers,
24125b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
24135b9c547cSRui Paulo if (!peer->macsec_desired)
24145b9c547cSRui Paulo continue;
24155b9c547cSRui Paulo
2416780fb4a2SCy Schubert if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED)
24175b9c547cSRui Paulo continue;
24185b9c547cSRui Paulo
2419780fb4a2SCy Schubert less_capability = (less_capability < peer->macsec_capability) ?
2420780fb4a2SCy Schubert less_capability : peer->macsec_capability;
2421c1d255d3SCy Schubert has_peer = true;
24225b9c547cSRui Paulo }
24235b9c547cSRui Paulo
24245b9c547cSRui Paulo if (has_peer) {
2425c1d255d3SCy Schubert participant->advised_desired = true;
24265b9c547cSRui Paulo participant->advised_capability = less_capability;
2427c1d255d3SCy Schubert kay->authenticated = false;
2428c1d255d3SCy Schubert kay->secured = true;
2429c1d255d3SCy Schubert kay->failed = false;
24305b9c547cSRui Paulo ieee802_1x_cp_connect_secure(kay->cp);
24315b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
24325b9c547cSRui Paulo } else {
2433c1d255d3SCy Schubert participant->advised_desired = false;
24345b9c547cSRui Paulo participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
2435c1d255d3SCy Schubert participant->to_use_sak = false;
2436c1d255d3SCy Schubert kay->authenticated = true;
2437c1d255d3SCy Schubert kay->secured = false;
2438c1d255d3SCy Schubert kay->failed = false;
24395b9c547cSRui Paulo kay->ltx_kn = 0;
24405b9c547cSRui Paulo kay->ltx_an = 0;
24415b9c547cSRui Paulo kay->lrx_kn = 0;
24425b9c547cSRui Paulo kay->lrx_an = 0;
24435b9c547cSRui Paulo kay->otx_kn = 0;
24445b9c547cSRui Paulo kay->otx_an = 0;
24455b9c547cSRui Paulo kay->orx_kn = 0;
24465b9c547cSRui Paulo kay->orx_an = 0;
24475b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp);
24485b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
24495b9c547cSRui Paulo }
24505b9c547cSRui Paulo
24515b9c547cSRui Paulo return 0;
24525b9c547cSRui Paulo }
24535b9c547cSRui Paulo
24545b9c547cSRui Paulo static const u8 pae_group_addr[ETH_ALEN] = {
24555b9c547cSRui Paulo 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
24565b9c547cSRui Paulo };
24575b9c547cSRui Paulo
24585b9c547cSRui Paulo
24595b9c547cSRui Paulo /**
24605b9c547cSRui Paulo * ieee802_1x_kay_encode_mkpdu -
24615b9c547cSRui Paulo */
24625b9c547cSRui Paulo static int
ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant * participant,struct wpabuf * pbuf)24635b9c547cSRui Paulo ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
24645b9c547cSRui Paulo struct wpabuf *pbuf)
24655b9c547cSRui Paulo {
24665b9c547cSRui Paulo unsigned int i;
24675b9c547cSRui Paulo struct ieee8023_hdr *ether_hdr;
24685b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr;
24695b9c547cSRui Paulo
24705b9c547cSRui Paulo ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
24715b9c547cSRui Paulo os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
24725b9c547cSRui Paulo os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
24735b9c547cSRui Paulo sizeof(ether_hdr->dest));
24745b9c547cSRui Paulo ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
24754bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
24764bc52338SCy Schubert " Ethertype=0x%x",
24774bc52338SCy Schubert MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src),
24784bc52338SCy Schubert be_to_host16(ether_hdr->ethertype));
24795b9c547cSRui Paulo
24805b9c547cSRui Paulo eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
24815b9c547cSRui Paulo eapol_hdr->version = EAPOL_VERSION;
24825b9c547cSRui Paulo eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
24834bc52338SCy Schubert eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf));
24844bc52338SCy Schubert wpa_printf(MSG_DEBUG,
24854bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
24864bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type,
24874bc52338SCy Schubert be_to_host16(eapol_hdr->length));
24885b9c547cSRui Paulo
2489780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2490780fb4a2SCy Schubert if (mka_body_handler[i].body_present &&
2491780fb4a2SCy Schubert mka_body_handler[i].body_present(participant)) {
2492780fb4a2SCy Schubert if (mka_body_handler[i].body_tx(participant, pbuf))
24935b9c547cSRui Paulo return -1;
24945b9c547cSRui Paulo }
24955b9c547cSRui Paulo }
24965b9c547cSRui Paulo
24975b9c547cSRui Paulo return 0;
24985b9c547cSRui Paulo }
24995b9c547cSRui Paulo
25004bc52338SCy Schubert
25015b9c547cSRui Paulo /**
25025b9c547cSRui Paulo * ieee802_1x_participant_send_mkpdu -
25035b9c547cSRui Paulo */
25045b9c547cSRui Paulo static int
ieee802_1x_participant_send_mkpdu(struct ieee802_1x_mka_participant * participant)25055b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(
25065b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant)
25075b9c547cSRui Paulo {
25085b9c547cSRui Paulo struct wpabuf *buf;
25095b9c547cSRui Paulo struct ieee802_1x_kay *kay = participant->kay;
25105b9c547cSRui Paulo size_t length = 0;
25115b9c547cSRui Paulo unsigned int i;
25125b9c547cSRui Paulo
25134bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)",
25144bc52338SCy Schubert kay->if_name);
25155b9c547cSRui Paulo length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
2516780fb4a2SCy Schubert for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
2517780fb4a2SCy Schubert if (mka_body_handler[i].body_present &&
2518780fb4a2SCy Schubert mka_body_handler[i].body_present(participant))
2519780fb4a2SCy Schubert length += mka_body_handler[i].body_length(participant);
25205b9c547cSRui Paulo }
25215b9c547cSRui Paulo
25225b9c547cSRui Paulo buf = wpabuf_alloc(length);
25235b9c547cSRui Paulo if (!buf) {
25245b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: out of memory");
25255b9c547cSRui Paulo return -1;
25265b9c547cSRui Paulo }
25275b9c547cSRui Paulo
25285b9c547cSRui Paulo if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
2529*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "KaY: encode MKPDU fail");
25305b9c547cSRui Paulo return -1;
25315b9c547cSRui Paulo }
25325b9c547cSRui Paulo
25334bc52338SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf);
25345b9c547cSRui Paulo l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
25355b9c547cSRui Paulo wpabuf_free(buf);
25365b9c547cSRui Paulo
2537c1d255d3SCy Schubert kay->active = true;
2538c1d255d3SCy Schubert participant->active = true;
25395b9c547cSRui Paulo
25405b9c547cSRui Paulo return 0;
25415b9c547cSRui Paulo }
25425b9c547cSRui Paulo
25435b9c547cSRui Paulo
25445b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
254585732ac8SCy Schubert
ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay * kay,struct transmit_sa * sa)254685732ac8SCy Schubert static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
254785732ac8SCy Schubert struct transmit_sa *sa)
254885732ac8SCy Schubert {
254985732ac8SCy Schubert secy_disable_transmit_sa(kay, sa);
255085732ac8SCy Schubert secy_delete_transmit_sa(kay, sa);
255185732ac8SCy Schubert ieee802_1x_kay_deinit_transmit_sa(sa);
255285732ac8SCy Schubert }
255385732ac8SCy Schubert
255485732ac8SCy Schubert
25555b9c547cSRui Paulo /**
25565b9c547cSRui Paulo * ieee802_1x_participant_timer -
25575b9c547cSRui Paulo */
ieee802_1x_participant_timer(void * eloop_ctx,void * timeout_ctx)25585b9c547cSRui Paulo static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
25595b9c547cSRui Paulo {
25605b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
25615b9c547cSRui Paulo struct ieee802_1x_kay *kay;
25625b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer, *pre_peer;
25635b9c547cSRui Paulo time_t now = time(NULL);
2564c1d255d3SCy Schubert bool lp_changed;
2565*a90b9d01SCy Schubert bool key_server_removed;
25665b9c547cSRui Paulo struct receive_sc *rxsc, *pre_rxsc;
25675b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa;
25685b9c547cSRui Paulo
25695b9c547cSRui Paulo participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
25705b9c547cSRui Paulo kay = participant->kay;
25714bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)",
25724bc52338SCy Schubert kay->if_name);
25735b9c547cSRui Paulo if (participant->cak_life) {
2574780fb4a2SCy Schubert if (now > participant->cak_life)
2575780fb4a2SCy Schubert goto delete_mka;
25765b9c547cSRui Paulo }
25775b9c547cSRui Paulo
25785b9c547cSRui Paulo /* should delete MKA instance if there are not live peers
25795b9c547cSRui Paulo * when the MKA life elapsed since its creating */
25805b9c547cSRui Paulo if (participant->mka_life) {
25815b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) {
2582780fb4a2SCy Schubert if (now > participant->mka_life)
2583780fb4a2SCy Schubert goto delete_mka;
25845b9c547cSRui Paulo } else {
25855b9c547cSRui Paulo participant->mka_life = 0;
25865b9c547cSRui Paulo }
25875b9c547cSRui Paulo }
25885b9c547cSRui Paulo
2589c1d255d3SCy Schubert lp_changed = false;
2590*a90b9d01SCy Schubert key_server_removed = false;
25915b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
25925b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
25935b9c547cSRui Paulo if (now > peer->expire) {
25945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
2595*a90b9d01SCy Schubert ieee802_1x_kay_dump_peer(peer);
25965b9c547cSRui Paulo dl_list_for_each_safe(rxsc, pre_rxsc,
25975b9c547cSRui Paulo &participant->rxsc_list,
25985b9c547cSRui Paulo struct receive_sc, list) {
2599780fb4a2SCy Schubert if (sci_equal(&rxsc->sci, &peer->sci)) {
26005b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(
26015b9c547cSRui Paulo participant, rxsc);
26025b9c547cSRui Paulo }
26035b9c547cSRui Paulo }
2604*a90b9d01SCy Schubert key_server_removed |= peer->is_key_server;
26055b9c547cSRui Paulo dl_list_del(&peer->list);
26065b9c547cSRui Paulo os_free(peer);
2607c1d255d3SCy Schubert lp_changed = true;
26085b9c547cSRui Paulo }
26095b9c547cSRui Paulo }
26105b9c547cSRui Paulo
2611*a90b9d01SCy Schubert /* The key server may be removed due to the ingress packets delay.
2612*a90b9d01SCy Schubert * In this situation, the endpoint of the key server may not be aware
2613*a90b9d01SCy Schubert * of this participant who has removed the key server from the peer
2614*a90b9d01SCy Schubert * list. Because the egress traffic is normal, the key server will not
2615*a90b9d01SCy Schubert * remove this participant from the peer list of the key server. So in
2616*a90b9d01SCy Schubert * the next MKA message, the key server will not dispatch a new SAK to
2617*a90b9d01SCy Schubert * this participant. And this participant cannot be aware that that is
2618*a90b9d01SCy Schubert * a new round of communication so it will not update its MI at
2619*a90b9d01SCy Schubert * re-adding the key server to its peer list. So we need to update MI
2620*a90b9d01SCy Schubert * to avoid the failure of the re-establishment MKA session. */
2621*a90b9d01SCy Schubert if (key_server_removed) {
2622*a90b9d01SCy Schubert if (!reset_participant_mi(participant))
2623*a90b9d01SCy Schubert wpa_printf(MSG_WARNING,
2624*a90b9d01SCy Schubert "KaY: Could not update MI on key server removal");
2625*a90b9d01SCy Schubert else
2626*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
2627*a90b9d01SCy Schubert "KaY: Update MI on key server removal");
2628*a90b9d01SCy Schubert }
2629*a90b9d01SCy Schubert
26305b9c547cSRui Paulo if (lp_changed) {
26315b9c547cSRui Paulo if (dl_list_empty(&participant->live_peers)) {
2632c1d255d3SCy Schubert participant->advised_desired = false;
26335b9c547cSRui Paulo participant->advised_capability =
26345b9c547cSRui Paulo MACSEC_CAP_NOT_IMPLEMENTED;
2635c1d255d3SCy Schubert participant->to_use_sak = false;
2636c1d255d3SCy Schubert participant->ltx = false;
2637c1d255d3SCy Schubert participant->lrx = false;
2638c1d255d3SCy Schubert participant->otx = false;
2639c1d255d3SCy Schubert participant->orx = false;
2640c1d255d3SCy Schubert participant->is_key_server = false;
2641c1d255d3SCy Schubert participant->is_elected = false;
2642c1d255d3SCy Schubert kay->authenticated = false;
2643c1d255d3SCy Schubert kay->secured = false;
2644c1d255d3SCy Schubert kay->failed = false;
26455b9c547cSRui Paulo kay->ltx_kn = 0;
26465b9c547cSRui Paulo kay->ltx_an = 0;
26475b9c547cSRui Paulo kay->lrx_kn = 0;
26485b9c547cSRui Paulo kay->lrx_an = 0;
26495b9c547cSRui Paulo kay->otx_kn = 0;
26505b9c547cSRui Paulo kay->otx_an = 0;
26515b9c547cSRui Paulo kay->orx_kn = 0;
26525b9c547cSRui Paulo kay->orx_an = 0;
26535b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa,
26545b9c547cSRui Paulo &participant->txsc->sa_list,
26555b9c547cSRui Paulo struct transmit_sa, list) {
265685732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa);
26575b9c547cSRui Paulo }
26585b9c547cSRui Paulo
265985732ac8SCy Schubert ieee802_1x_cp_connect_pending(kay->cp);
26605b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
26615b9c547cSRui Paulo } else {
26625b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant);
26635b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant);
26645b9c547cSRui Paulo }
26655b9c547cSRui Paulo }
26665b9c547cSRui Paulo
26675b9c547cSRui Paulo dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
26685b9c547cSRui Paulo struct ieee802_1x_kay_peer, list) {
26695b9c547cSRui Paulo if (now > peer->expire) {
26705b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
2671*a90b9d01SCy Schubert ieee802_1x_kay_dump_peer(peer);
26725b9c547cSRui Paulo dl_list_del(&peer->list);
26735b9c547cSRui Paulo os_free(peer);
26745b9c547cSRui Paulo }
26755b9c547cSRui Paulo }
26765b9c547cSRui Paulo
26774bc52338SCy Schubert if (participant->new_sak && participant->is_key_server) {
26785b9c547cSRui Paulo if (!ieee802_1x_kay_generate_new_sak(participant))
2679c1d255d3SCy Schubert participant->to_dist_sak = true;
26805b9c547cSRui Paulo
2681c1d255d3SCy Schubert participant->new_sak = false;
26825b9c547cSRui Paulo }
26835b9c547cSRui Paulo
268485732ac8SCy Schubert if (participant->retry_count < MAX_RETRY_CNT ||
268585732ac8SCy Schubert participant->mode == PSK) {
26865b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(participant);
26875b9c547cSRui Paulo participant->retry_count++;
26885b9c547cSRui Paulo }
26895b9c547cSRui Paulo
26904bc52338SCy Schubert eloop_register_timeout(kay->mka_hello_time / 1000, 0,
26915b9c547cSRui Paulo ieee802_1x_participant_timer,
26925b9c547cSRui Paulo participant, NULL);
2693780fb4a2SCy Schubert
2694780fb4a2SCy Schubert return;
2695780fb4a2SCy Schubert
2696780fb4a2SCy Schubert delete_mka:
2697c1d255d3SCy Schubert kay->authenticated = false;
2698c1d255d3SCy Schubert kay->secured = false;
2699c1d255d3SCy Schubert kay->failed = true;
2700780fb4a2SCy Schubert ieee802_1x_kay_delete_mka(kay, &participant->ckn);
27015b9c547cSRui Paulo }
27025b9c547cSRui Paulo
27035b9c547cSRui Paulo
27045b9c547cSRui Paulo /**
27055b9c547cSRui Paulo * ieee802_1x_kay_init_transmit_sa -
27065b9c547cSRui Paulo */
27075b9c547cSRui Paulo static struct transmit_sa *
ieee802_1x_kay_init_transmit_sa(struct transmit_sc * psc,u8 an,u32 next_PN,struct data_key * key)27085b9c547cSRui Paulo ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
27095b9c547cSRui Paulo struct data_key *key)
27105b9c547cSRui Paulo {
27115b9c547cSRui Paulo struct transmit_sa *psa;
27125b9c547cSRui Paulo
2713c1d255d3SCy Schubert key->tx_latest = true;
2714c1d255d3SCy Schubert key->rx_latest = true;
27155b9c547cSRui Paulo
27165b9c547cSRui Paulo psa = os_zalloc(sizeof(*psa));
27175b9c547cSRui Paulo if (!psa) {
27185b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
27195b9c547cSRui Paulo return NULL;
27205b9c547cSRui Paulo }
27215b9c547cSRui Paulo
27225b9c547cSRui Paulo if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
27235b9c547cSRui Paulo key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
2724c1d255d3SCy Schubert psa->confidentiality = true;
27255b9c547cSRui Paulo else
2726c1d255d3SCy Schubert psa->confidentiality = false;
27275b9c547cSRui Paulo
27285b9c547cSRui Paulo psa->an = an;
272985732ac8SCy Schubert ieee802_1x_kay_use_data_key(key);
27305b9c547cSRui Paulo psa->pkey = key;
27315b9c547cSRui Paulo psa->next_pn = next_PN;
27325b9c547cSRui Paulo psa->sc = psc;
27335b9c547cSRui Paulo
27345b9c547cSRui Paulo os_get_time(&psa->created_time);
2735c1d255d3SCy Schubert psa->in_use = false;
27365b9c547cSRui Paulo
27375b9c547cSRui Paulo dl_list_add(&psc->sa_list, &psa->list);
27385b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
27394bc52338SCy Schubert "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC",
274085732ac8SCy Schubert an, next_PN);
27415b9c547cSRui Paulo
27425b9c547cSRui Paulo return psa;
27435b9c547cSRui Paulo }
27445b9c547cSRui Paulo
27455b9c547cSRui Paulo
27465b9c547cSRui Paulo /**
27475b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sa -
27485b9c547cSRui Paulo */
ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa * psa)27495b9c547cSRui Paulo static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
27505b9c547cSRui Paulo {
275185732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(psa->pkey);
27525b9c547cSRui Paulo psa->pkey = NULL;
27535b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
2754780fb4a2SCy Schubert "KaY: Delete transmit SA(an: %hhu) of SC",
2755780fb4a2SCy Schubert psa->an);
27565b9c547cSRui Paulo dl_list_del(&psa->list);
27575b9c547cSRui Paulo os_free(psa);
27585b9c547cSRui Paulo }
27595b9c547cSRui Paulo
27605b9c547cSRui Paulo
27615b9c547cSRui Paulo /**
27625b9c547cSRui Paulo * init_transmit_sc -
27635b9c547cSRui Paulo */
27645b9c547cSRui Paulo static struct transmit_sc *
ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci * sci)276585732ac8SCy Schubert ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
27665b9c547cSRui Paulo {
27675b9c547cSRui Paulo struct transmit_sc *psc;
27685b9c547cSRui Paulo
27695b9c547cSRui Paulo psc = os_zalloc(sizeof(*psc));
27705b9c547cSRui Paulo if (!psc) {
27715b9c547cSRui Paulo wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
27725b9c547cSRui Paulo return NULL;
27735b9c547cSRui Paulo }
27745b9c547cSRui Paulo os_memcpy(&psc->sci, sci, sizeof(psc->sci));
27755b9c547cSRui Paulo
27765b9c547cSRui Paulo os_get_time(&psc->created_time);
2777c1d255d3SCy Schubert psc->transmitting = false;
2778c1d255d3SCy Schubert psc->encoding_sa = false;
2779c1d255d3SCy Schubert psc->enciphering_sa = false;
27805b9c547cSRui Paulo
27815b9c547cSRui Paulo dl_list_init(&psc->sa_list);
27824bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s",
27834bc52338SCy Schubert sci_txt(&psc->sci));
27845b9c547cSRui Paulo
27855b9c547cSRui Paulo return psc;
27865b9c547cSRui Paulo }
27875b9c547cSRui Paulo
27885b9c547cSRui Paulo
27895b9c547cSRui Paulo /**
27905b9c547cSRui Paulo * ieee802_1x_kay_deinit_transmit_sc -
27915b9c547cSRui Paulo */
27925b9c547cSRui Paulo static void
ieee802_1x_kay_deinit_transmit_sc(struct ieee802_1x_mka_participant * participant,struct transmit_sc * psc)27935b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(
27945b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
27955b9c547cSRui Paulo {
27965b9c547cSRui Paulo struct transmit_sa *psa, *tmp;
27975b9c547cSRui Paulo
279885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
279985732ac8SCy Schubert dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
280085732ac8SCy Schubert ieee802_1x_delete_transmit_sa(participant->kay, psa);
28015b9c547cSRui Paulo
280285732ac8SCy Schubert secy_delete_transmit_sc(participant->kay, psc);
28035b9c547cSRui Paulo os_free(psc);
28045b9c547cSRui Paulo }
28055b9c547cSRui Paulo
28065b9c547cSRui Paulo
28075b9c547cSRui Paulo /****************** Interface between CP and KAY *********************/
28085b9c547cSRui Paulo /**
28095b9c547cSRui Paulo * ieee802_1x_kay_set_latest_sa_attr -
28105b9c547cSRui Paulo */
ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * lki,u8 lan,bool ltx,bool lrx)28115b9c547cSRui Paulo int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
28125b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki, u8 lan,
2813c1d255d3SCy Schubert bool ltx, bool lrx)
28145b9c547cSRui Paulo {
28155b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
28165b9c547cSRui Paulo
28175b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
28185b9c547cSRui Paulo if (!principal)
28195b9c547cSRui Paulo return -1;
28205b9c547cSRui Paulo
28215b9c547cSRui Paulo if (!lki)
28225b9c547cSRui Paulo os_memset(&principal->lki, 0, sizeof(principal->lki));
28235b9c547cSRui Paulo else
28245b9c547cSRui Paulo os_memcpy(&principal->lki, lki, sizeof(principal->lki));
28255b9c547cSRui Paulo
28265b9c547cSRui Paulo principal->lan = lan;
28275b9c547cSRui Paulo principal->ltx = ltx;
28285b9c547cSRui Paulo principal->lrx = lrx;
28295b9c547cSRui Paulo if (!lki) {
28305b9c547cSRui Paulo kay->ltx_kn = 0;
28315b9c547cSRui Paulo kay->lrx_kn = 0;
28325b9c547cSRui Paulo } else {
28335b9c547cSRui Paulo kay->ltx_kn = lki->kn;
28345b9c547cSRui Paulo kay->lrx_kn = lki->kn;
28355b9c547cSRui Paulo }
28365b9c547cSRui Paulo kay->ltx_an = lan;
28375b9c547cSRui Paulo kay->lrx_an = lan;
28385b9c547cSRui Paulo
28395b9c547cSRui Paulo return 0;
28405b9c547cSRui Paulo }
28415b9c547cSRui Paulo
28425b9c547cSRui Paulo
28435b9c547cSRui Paulo /**
28445b9c547cSRui Paulo * ieee802_1x_kay_set_old_sa_attr -
28455b9c547cSRui Paulo */
ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * oki,u8 oan,bool otx,bool orx)28465b9c547cSRui Paulo int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
28475b9c547cSRui Paulo struct ieee802_1x_mka_ki *oki,
2848c1d255d3SCy Schubert u8 oan, bool otx, bool orx)
28495b9c547cSRui Paulo {
28505b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
28515b9c547cSRui Paulo
28525b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
28535b9c547cSRui Paulo if (!principal)
28545b9c547cSRui Paulo return -1;
28555b9c547cSRui Paulo
28565b9c547cSRui Paulo if (!oki)
28575b9c547cSRui Paulo os_memset(&principal->oki, 0, sizeof(principal->oki));
28585b9c547cSRui Paulo else
28595b9c547cSRui Paulo os_memcpy(&principal->oki, oki, sizeof(principal->oki));
28605b9c547cSRui Paulo
28615b9c547cSRui Paulo principal->oan = oan;
28625b9c547cSRui Paulo principal->otx = otx;
28635b9c547cSRui Paulo principal->orx = orx;
28645b9c547cSRui Paulo
28655b9c547cSRui Paulo if (!oki) {
28665b9c547cSRui Paulo kay->otx_kn = 0;
28675b9c547cSRui Paulo kay->orx_kn = 0;
28685b9c547cSRui Paulo } else {
28695b9c547cSRui Paulo kay->otx_kn = oki->kn;
28705b9c547cSRui Paulo kay->orx_kn = oki->kn;
28715b9c547cSRui Paulo }
28725b9c547cSRui Paulo kay->otx_an = oan;
28735b9c547cSRui Paulo kay->orx_an = oan;
28745b9c547cSRui Paulo
28755b9c547cSRui Paulo return 0;
28765b9c547cSRui Paulo }
28775b9c547cSRui Paulo
28785b9c547cSRui Paulo
lookup_txsa_by_an(struct transmit_sc * txsc,u8 an)287985732ac8SCy Schubert static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
288085732ac8SCy Schubert {
288185732ac8SCy Schubert struct transmit_sa *txsa;
288285732ac8SCy Schubert
288385732ac8SCy Schubert dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
288485732ac8SCy Schubert if (txsa->an == an)
288585732ac8SCy Schubert return txsa;
288685732ac8SCy Schubert }
288785732ac8SCy Schubert
288885732ac8SCy Schubert return NULL;
288985732ac8SCy Schubert }
289085732ac8SCy Schubert
289185732ac8SCy Schubert
lookup_rxsa_by_an(struct receive_sc * rxsc,u8 an)289285732ac8SCy Schubert static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
289385732ac8SCy Schubert {
289485732ac8SCy Schubert struct receive_sa *rxsa;
289585732ac8SCy Schubert
289685732ac8SCy Schubert dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
289785732ac8SCy Schubert if (rxsa->an == an)
289885732ac8SCy Schubert return rxsa;
289985732ac8SCy Schubert }
290085732ac8SCy Schubert
290185732ac8SCy Schubert return NULL;
290285732ac8SCy Schubert }
290385732ac8SCy Schubert
290485732ac8SCy Schubert
29055b9c547cSRui Paulo /**
29065b9c547cSRui Paulo * ieee802_1x_kay_create_sas -
29075b9c547cSRui Paulo */
ieee802_1x_kay_create_sas(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * lki)29085b9c547cSRui Paulo int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
29095b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki)
29105b9c547cSRui Paulo {
29115b9c547cSRui Paulo struct data_key *sa_key, *latest_sak;
29125b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
29135b9c547cSRui Paulo struct receive_sc *rxsc;
29145b9c547cSRui Paulo struct receive_sa *rxsa;
29155b9c547cSRui Paulo struct transmit_sa *txsa;
29165b9c547cSRui Paulo
29175b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
29185b9c547cSRui Paulo if (!principal)
29195b9c547cSRui Paulo return -1;
29205b9c547cSRui Paulo
29215b9c547cSRui Paulo latest_sak = NULL;
29225b9c547cSRui Paulo dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
29235b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, lki)) {
2924c1d255d3SCy Schubert sa_key->rx_latest = true;
2925c1d255d3SCy Schubert sa_key->tx_latest = true;
29265b9c547cSRui Paulo latest_sak = sa_key;
2927c1d255d3SCy Schubert principal->to_use_sak = true;
29285b9c547cSRui Paulo } else {
2929c1d255d3SCy Schubert sa_key->rx_latest = false;
2930c1d255d3SCy Schubert sa_key->tx_latest = false;
29315b9c547cSRui Paulo }
29325b9c547cSRui Paulo }
29335b9c547cSRui Paulo if (!latest_sak) {
29344bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: lki related sak not found");
29355b9c547cSRui Paulo return -1;
29365b9c547cSRui Paulo }
29375b9c547cSRui Paulo
29385b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
293985732ac8SCy Schubert while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
294085732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa);
294185732ac8SCy Schubert
29425b9c547cSRui Paulo rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
29435b9c547cSRui Paulo latest_sak);
29445b9c547cSRui Paulo if (!rxsa)
29455b9c547cSRui Paulo return -1;
29465b9c547cSRui Paulo
29475b9c547cSRui Paulo secy_create_receive_sa(kay, rxsa);
29485b9c547cSRui Paulo }
29495b9c547cSRui Paulo
295085732ac8SCy Schubert while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
295185732ac8SCy Schubert NULL)
295285732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa);
295385732ac8SCy Schubert
29545b9c547cSRui Paulo txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
29554bc52338SCy Schubert latest_sak->next_pn ?
29564bc52338SCy Schubert latest_sak->next_pn : 1,
29574bc52338SCy Schubert latest_sak);
29585b9c547cSRui Paulo if (!txsa)
29595b9c547cSRui Paulo return -1;
29605b9c547cSRui Paulo
29615b9c547cSRui Paulo secy_create_transmit_sa(kay, txsa);
29625b9c547cSRui Paulo
29635b9c547cSRui Paulo
29645b9c547cSRui Paulo
29655b9c547cSRui Paulo return 0;
29665b9c547cSRui Paulo }
29675b9c547cSRui Paulo
29685b9c547cSRui Paulo
29695b9c547cSRui Paulo /**
29705b9c547cSRui Paulo * ieee802_1x_kay_delete_sas -
29715b9c547cSRui Paulo */
ieee802_1x_kay_delete_sas(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * ki)29725b9c547cSRui Paulo int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
29735b9c547cSRui Paulo struct ieee802_1x_mka_ki *ki)
29745b9c547cSRui Paulo {
29755b9c547cSRui Paulo struct data_key *sa_key, *pre_key;
29765b9c547cSRui Paulo struct transmit_sa *txsa, *pre_txsa;
29775b9c547cSRui Paulo struct receive_sa *rxsa, *pre_rxsa;
29785b9c547cSRui Paulo struct receive_sc *rxsc;
29795b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
29805b9c547cSRui Paulo
29815b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
29825b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
29835b9c547cSRui Paulo if (!principal)
29845b9c547cSRui Paulo return -1;
29855b9c547cSRui Paulo
29865b9c547cSRui Paulo /* remove the transmit sa */
29875b9c547cSRui Paulo dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
29885b9c547cSRui Paulo struct transmit_sa, list) {
298985732ac8SCy Schubert if (is_ki_equal(&txsa->pkey->key_identifier, ki))
299085732ac8SCy Schubert ieee802_1x_delete_transmit_sa(kay, txsa);
29915b9c547cSRui Paulo }
29925b9c547cSRui Paulo
29935b9c547cSRui Paulo /* remove the receive sa */
29945b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
29955b9c547cSRui Paulo dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
29965b9c547cSRui Paulo struct receive_sa, list) {
299785732ac8SCy Schubert if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
299885732ac8SCy Schubert ieee802_1x_delete_receive_sa(kay, rxsa);
29995b9c547cSRui Paulo }
30005b9c547cSRui Paulo }
30015b9c547cSRui Paulo
30025b9c547cSRui Paulo /* remove the sak */
30035b9c547cSRui Paulo dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
30045b9c547cSRui Paulo struct data_key, list) {
30055b9c547cSRui Paulo if (is_ki_equal(&sa_key->key_identifier, ki)) {
30064bc52338SCy Schubert if (principal->new_key == sa_key)
30074bc52338SCy Schubert principal->new_key = NULL;
300885732ac8SCy Schubert dl_list_del(&sa_key->list);
30095b9c547cSRui Paulo ieee802_1x_kay_deinit_data_key(sa_key);
30105b9c547cSRui Paulo break;
30115b9c547cSRui Paulo }
30125b9c547cSRui Paulo }
30135b9c547cSRui Paulo
30145b9c547cSRui Paulo return 0;
30155b9c547cSRui Paulo }
30165b9c547cSRui Paulo
30175b9c547cSRui Paulo
30185b9c547cSRui Paulo /**
30195b9c547cSRui Paulo * ieee802_1x_kay_enable_tx_sas -
30205b9c547cSRui Paulo */
ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * lki)30215b9c547cSRui Paulo int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
30225b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki)
30235b9c547cSRui Paulo {
30245b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
30255b9c547cSRui Paulo struct transmit_sa *txsa;
30265b9c547cSRui Paulo
30275b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
30285b9c547cSRui Paulo if (!principal)
30295b9c547cSRui Paulo return -1;
30305b9c547cSRui Paulo
30315b9c547cSRui Paulo dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
30325b9c547cSRui Paulo list) {
30335b9c547cSRui Paulo if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
3034c1d255d3SCy Schubert txsa->in_use = true;
30355b9c547cSRui Paulo secy_enable_transmit_sa(kay, txsa);
30365b9c547cSRui Paulo ieee802_1x_cp_set_usingtransmitas(
3037c1d255d3SCy Schubert principal->kay->cp, true);
30385b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp);
30395b9c547cSRui Paulo }
30405b9c547cSRui Paulo }
30415b9c547cSRui Paulo
30425b9c547cSRui Paulo return 0;
30435b9c547cSRui Paulo }
30445b9c547cSRui Paulo
30455b9c547cSRui Paulo
30465b9c547cSRui Paulo /**
30475b9c547cSRui Paulo * ieee802_1x_kay_enable_rx_sas -
30485b9c547cSRui Paulo */
ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay * kay,struct ieee802_1x_mka_ki * lki)30495b9c547cSRui Paulo int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
30505b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki)
30515b9c547cSRui Paulo {
30525b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
30535b9c547cSRui Paulo struct receive_sa *rxsa;
30545b9c547cSRui Paulo struct receive_sc *rxsc;
30555b9c547cSRui Paulo
30565b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
30575b9c547cSRui Paulo if (!principal)
30585b9c547cSRui Paulo return -1;
30595b9c547cSRui Paulo
30605b9c547cSRui Paulo dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
30615b9c547cSRui Paulo dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
30625b9c547cSRui Paulo {
30635b9c547cSRui Paulo if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
3064c1d255d3SCy Schubert rxsa->in_use = true;
30655b9c547cSRui Paulo secy_enable_receive_sa(kay, rxsa);
30665b9c547cSRui Paulo ieee802_1x_cp_set_usingreceivesas(
3067c1d255d3SCy Schubert principal->kay->cp, true);
30685b9c547cSRui Paulo ieee802_1x_cp_sm_step(principal->kay->cp);
30695b9c547cSRui Paulo }
30705b9c547cSRui Paulo }
30715b9c547cSRui Paulo }
30725b9c547cSRui Paulo
30735b9c547cSRui Paulo return 0;
30745b9c547cSRui Paulo }
30755b9c547cSRui Paulo
30765b9c547cSRui Paulo
30775b9c547cSRui Paulo /**
30785b9c547cSRui Paulo * ieee802_1x_kay_enable_new_info -
30795b9c547cSRui Paulo */
ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay * kay)30805b9c547cSRui Paulo int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
30815b9c547cSRui Paulo {
30825b9c547cSRui Paulo struct ieee802_1x_mka_participant *principal;
30835b9c547cSRui Paulo
30845b9c547cSRui Paulo principal = ieee802_1x_kay_get_principal_participant(kay);
30855b9c547cSRui Paulo if (!principal)
30865b9c547cSRui Paulo return -1;
30875b9c547cSRui Paulo
308885732ac8SCy Schubert if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
30895b9c547cSRui Paulo ieee802_1x_participant_send_mkpdu(principal);
30905b9c547cSRui Paulo principal->retry_count++;
30915b9c547cSRui Paulo }
30925b9c547cSRui Paulo
30935b9c547cSRui Paulo return 0;
30945b9c547cSRui Paulo }
30955b9c547cSRui Paulo
30965b9c547cSRui Paulo
30975b9c547cSRui Paulo /**
30984b72b91aSCy Schubert * ieee802_1x_kay_mkpdu_validity_check -
30994b72b91aSCy Schubert * Validity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
31004bc52338SCy Schubert * MKPDUs)
31015b9c547cSRui Paulo */
ieee802_1x_kay_mkpdu_validity_check(struct ieee802_1x_kay * kay,const u8 * buf,size_t len)31024b72b91aSCy Schubert static int ieee802_1x_kay_mkpdu_validity_check(struct ieee802_1x_kay *kay,
31035b9c547cSRui Paulo const u8 *buf, size_t len)
31045b9c547cSRui Paulo {
31055b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr;
31065b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr;
31075b9c547cSRui Paulo struct ieee802_1x_mka_hdr *mka_hdr;
31085b9c547cSRui Paulo struct ieee802_1x_mka_basic_body *body;
31095b9c547cSRui Paulo size_t mka_msg_len;
31105b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
31115b9c547cSRui Paulo size_t body_len;
311285732ac8SCy Schubert size_t ckn_len;
31135b9c547cSRui Paulo u8 icv[MAX_ICV_LEN];
31144bc52338SCy Schubert const u8 *msg_icv;
31155b9c547cSRui Paulo
31164bc52338SCy Schubert /* len > eth+eapol header already verified in kay_l2_receive();
31174bc52338SCy Schubert * likewise, eapol_hdr->length validated there */
31185b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf;
31195b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
31205b9c547cSRui Paulo mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
31215b9c547cSRui Paulo
31224bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
31234bc52338SCy Schubert " Ethertype=0x%x",
31244bc52338SCy Schubert MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src),
31254bc52338SCy Schubert be_to_host16(eth_hdr->ethertype));
31264bc52338SCy Schubert
31274bc52338SCy Schubert /* the destination address shall not be an individual address */
3128*a90b9d01SCy Schubert if (!ether_addr_equal(eth_hdr->dest, pae_group_addr)) {
31294bc52338SCy Schubert wpa_printf(MSG_DEBUG,
31305b9c547cSRui Paulo "KaY: ethernet destination address is not PAE group address");
31315b9c547cSRui Paulo return -1;
31325b9c547cSRui Paulo }
31335b9c547cSRui Paulo
31344bc52338SCy Schubert wpa_printf(MSG_DEBUG,
31354bc52338SCy Schubert "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
31364bc52338SCy Schubert eapol_hdr->version, eapol_hdr->type,
31374bc52338SCy Schubert be_to_host16(eapol_hdr->length));
31384bc52338SCy Schubert
31394bc52338SCy Schubert /* MKPDU shall not be less than 32 octets */
31405b9c547cSRui Paulo mka_msg_len = be_to_host16(eapol_hdr->length);
31415b9c547cSRui Paulo if (mka_msg_len < 32) {
31424bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets");
31435b9c547cSRui Paulo return -1;
31445b9c547cSRui Paulo }
31454bc52338SCy Schubert /* MKPDU shall be a multiple of 4 octets */
31465b9c547cSRui Paulo if ((mka_msg_len % 4) != 0) {
31474bc52338SCy Schubert wpa_printf(MSG_DEBUG,
31485b9c547cSRui Paulo "KaY: MKPDU is not multiple of 4 octets");
31495b9c547cSRui Paulo return -1;
31505b9c547cSRui Paulo }
31515b9c547cSRui Paulo
31524bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)",
31534bc52338SCy Schubert mka_hdr, mka_msg_len);
31544bc52338SCy Schubert
31554bc52338SCy Schubert /* Room for body_len already verified in kay_l2_receive() */
31565b9c547cSRui Paulo body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
31575b9c547cSRui Paulo body_len = get_mka_param_body_len(body);
31585b9c547cSRui Paulo /* EAPOL-MKA body should comprise basic parameter set and ICV */
31595b9c547cSRui Paulo if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
31605b9c547cSRui Paulo wpa_printf(MSG_ERROR,
3161780fb4a2SCy 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",
3162780fb4a2SCy Schubert mka_msg_len, MKA_HDR_LEN,
3163780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN);
31645b9c547cSRui Paulo return -1;
31655b9c547cSRui Paulo }
31665b9c547cSRui Paulo
316785732ac8SCy Schubert if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
316885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
316985732ac8SCy Schubert body_len);
317085732ac8SCy Schubert return -1;
317185732ac8SCy Schubert }
317285732ac8SCy Schubert ckn_len = body_len -
317385732ac8SCy Schubert (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
317485732ac8SCy Schubert if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
31754bc52338SCy Schubert wpa_printf(MSG_WARNING,
317685732ac8SCy Schubert "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
317785732ac8SCy Schubert ckn_len, MAX_CKN_LEN);
317885732ac8SCy Schubert return -1;
317985732ac8SCy Schubert }
318085732ac8SCy Schubert
31814bc52338SCy Schubert ieee802_1x_mka_dump_basic_body(body);
31824bc52338SCy Schubert
31835b9c547cSRui Paulo /* CKN should be owned by I */
318485732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
31855b9c547cSRui Paulo if (!participant) {
31864bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA");
31875b9c547cSRui Paulo return -1;
31885b9c547cSRui Paulo }
31895b9c547cSRui Paulo
31905b9c547cSRui Paulo /* algorithm agility check */
31915b9c547cSRui Paulo if (os_memcmp(body->algo_agility, mka_algo_agility,
31925b9c547cSRui Paulo sizeof(body->algo_agility)) != 0) {
31934bc52338SCy Schubert wpa_printf(MSG_INFO,
31944bc52338SCy Schubert "KaY: Peer's algorithm agility (%s) not supported",
31954bc52338SCy Schubert algo_agility_txt(body->algo_agility));
31965b9c547cSRui Paulo return -1;
31975b9c547cSRui Paulo }
31985b9c547cSRui Paulo
31995b9c547cSRui Paulo /* ICV check */
32005b9c547cSRui Paulo /*
32015b9c547cSRui Paulo * The ICV will comprise the final octets of the packet body, whatever
32025b9c547cSRui Paulo * its size, not the fixed length 16 octets, indicated by the EAPOL
32035b9c547cSRui Paulo * packet body length.
32045b9c547cSRui Paulo */
32054bc52338SCy Schubert if (len < mka_alg_tbl[kay->mka_algindex].icv_len ||
32064bc52338SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_hash(
32074bc52338SCy Schubert participant->ick.key, participant->ick.len,
32085b9c547cSRui Paulo buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
32094bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV");
32105b9c547cSRui Paulo return -1;
32115b9c547cSRui Paulo }
3212780fb4a2SCy Schubert
32134bc52338SCy Schubert msg_icv = ieee802_1x_mka_decode_icv_body(participant,
32144bc52338SCy Schubert (const u8 *) mka_hdr,
32155b9c547cSRui Paulo mka_msg_len);
3216780fb4a2SCy Schubert if (!msg_icv) {
32174bc52338SCy Schubert wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it");
32185b9c547cSRui Paulo return -1;
32195b9c547cSRui Paulo }
32204bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Received ICV",
32214bc52338SCy Schubert msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len);
3222780fb4a2SCy Schubert if (os_memcmp_const(msg_icv, icv,
3223780fb4a2SCy Schubert mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
32244bc52338SCy Schubert wpa_printf(MSG_WARNING,
3225780fb4a2SCy Schubert "KaY: Computed ICV is not equal to Received ICV");
32264bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV",
32274bc52338SCy Schubert icv, mka_alg_tbl[kay->mka_algindex].icv_len);
32285b9c547cSRui Paulo return -1;
32295b9c547cSRui Paulo }
32305b9c547cSRui Paulo
32315b9c547cSRui Paulo return 0;
32325b9c547cSRui Paulo }
32335b9c547cSRui Paulo
32345b9c547cSRui Paulo
32355b9c547cSRui Paulo /**
32365b9c547cSRui Paulo * ieee802_1x_kay_decode_mkpdu -
32375b9c547cSRui Paulo */
ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay * kay,const u8 * buf,size_t len)32385b9c547cSRui Paulo static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
32395b9c547cSRui Paulo const u8 *buf, size_t len)
32405b9c547cSRui Paulo {
32415b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
32425b9c547cSRui Paulo struct ieee802_1x_mka_hdr *hdr;
32434bc52338SCy Schubert struct ieee802_1x_kay_peer *peer;
32445b9c547cSRui Paulo size_t body_len;
32455b9c547cSRui Paulo size_t left_len;
3246780fb4a2SCy Schubert u8 body_type;
32475b9c547cSRui Paulo int i;
32485b9c547cSRui Paulo const u8 *pos;
3249c1d255d3SCy Schubert bool handled[256];
3250c1d255d3SCy Schubert bool bad_sak_use = false; /* Error detected while processing SAK Use
32514bc52338SCy Schubert * parameter set */
3252c1d255d3SCy Schubert bool i_in_peerlist, is_in_live_peer, is_in_potential_peer;
32535b9c547cSRui Paulo
32544bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)",
32554bc52338SCy Schubert kay->if_name);
32564b72b91aSCy Schubert if (ieee802_1x_kay_mkpdu_validity_check(kay, buf, len))
32575b9c547cSRui Paulo return -1;
32585b9c547cSRui Paulo
32595b9c547cSRui Paulo /* handle basic parameter set */
32605b9c547cSRui Paulo pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
32615b9c547cSRui Paulo left_len = len - sizeof(struct ieee8023_hdr) -
32625b9c547cSRui Paulo sizeof(struct ieee802_1x_hdr);
32635b9c547cSRui Paulo participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
32645b9c547cSRui Paulo if (!participant)
32655b9c547cSRui Paulo return -1;
32665b9c547cSRui Paulo
32675b9c547cSRui Paulo /* to skip basic parameter set */
32685b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos;
32694bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
32704bc52338SCy Schubert if (left_len < body_len + MKA_HDR_LEN)
32714bc52338SCy Schubert return -1;
32725b9c547cSRui Paulo pos += body_len + MKA_HDR_LEN;
32735b9c547cSRui Paulo left_len -= body_len + MKA_HDR_LEN;
32745b9c547cSRui Paulo
32755b9c547cSRui Paulo /* check i am in the peer's peer list */
32764bc52338SCy Schubert i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos,
32774bc52338SCy Schubert left_len);
32784bc52338SCy Schubert is_in_live_peer = ieee802_1x_kay_is_in_live_peer(
32794bc52338SCy Schubert participant, participant->current_peer_id.mi);
32804bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s",
32814bc52338SCy Schubert yes_no(i_in_peerlist), yes_no(is_in_live_peer));
32824bc52338SCy Schubert if (i_in_peerlist && !is_in_live_peer) {
3283780fb4a2SCy Schubert /* accept the peer as live peer */
32844bc52338SCy Schubert is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer(
32854bc52338SCy Schubert participant, participant->current_peer_id.mi);
32864bc52338SCy Schubert if (is_in_potential_peer) {
3287780fb4a2SCy Schubert if (!ieee802_1x_kay_move_live_peer(
3288780fb4a2SCy Schubert participant,
3289780fb4a2SCy Schubert participant->current_peer_id.mi,
3290780fb4a2SCy Schubert be_to_host32(participant->
3291780fb4a2SCy Schubert current_peer_id.mn)))
3292780fb4a2SCy Schubert return -1;
3293780fb4a2SCy Schubert } else if (!ieee802_1x_kay_create_live_peer(
32945b9c547cSRui Paulo participant, participant->current_peer_id.mi,
3295780fb4a2SCy Schubert be_to_host32(participant->
3296780fb4a2SCy Schubert current_peer_id.mn))) {
3297780fb4a2SCy Schubert return -1;
3298780fb4a2SCy Schubert }
3299780fb4a2SCy Schubert
33005b9c547cSRui Paulo ieee802_1x_kay_elect_key_server(participant);
33015b9c547cSRui Paulo ieee802_1x_kay_decide_macsec_use(participant);
33025b9c547cSRui Paulo }
33035b9c547cSRui Paulo
33045b9c547cSRui Paulo /*
33055b9c547cSRui Paulo * Handle other parameter set than basic parameter set.
33065b9c547cSRui Paulo * Each parameter set should be present only once.
33075b9c547cSRui Paulo */
33085b9c547cSRui Paulo for (i = 0; i < 256; i++)
3309c1d255d3SCy Schubert handled[i] = false;
33105b9c547cSRui Paulo
3311c1d255d3SCy Schubert handled[0] = true;
3312780fb4a2SCy Schubert for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
3313780fb4a2SCy Schubert pos += body_len + MKA_HDR_LEN,
3314780fb4a2SCy Schubert left_len -= body_len + MKA_HDR_LEN) {
33155b9c547cSRui Paulo hdr = (struct ieee802_1x_mka_hdr *) pos;
33164bc52338SCy Schubert body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
33175b9c547cSRui Paulo body_type = get_mka_param_body_type(hdr);
33185b9c547cSRui Paulo
33195b9c547cSRui Paulo if (body_type == MKA_ICV_INDICATOR)
33205b9c547cSRui Paulo return 0;
33215b9c547cSRui Paulo
33225b9c547cSRui Paulo if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
33235b9c547cSRui Paulo wpa_printf(MSG_ERROR,
3324780fb4a2SCy 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",
3325780fb4a2SCy Schubert left_len, MKA_HDR_LEN,
3326780fb4a2SCy Schubert body_len, DEFAULT_ICV_LEN);
332785732ac8SCy Schubert return -1;
33285b9c547cSRui Paulo }
33295b9c547cSRui Paulo
33304bc52338SCy Schubert if (handled[body_type]) {
33314bc52338SCy Schubert wpa_printf(MSG_DEBUG,
33324bc52338SCy Schubert "KaY: Ignore duplicated body type %u",
33334bc52338SCy Schubert body_type);
3334780fb4a2SCy Schubert continue;
33354bc52338SCy Schubert }
33365b9c547cSRui Paulo
3337c1d255d3SCy Schubert handled[body_type] = true;
3338780fb4a2SCy Schubert if (body_type < ARRAY_SIZE(mka_body_handler) &&
3339780fb4a2SCy Schubert mka_body_handler[body_type].body_rx) {
33404bc52338SCy Schubert if (mka_body_handler[body_type].body_rx
33414bc52338SCy Schubert (participant, pos, left_len) != 0) {
33424bc52338SCy Schubert /* Handle parameter set failure */
33434bc52338SCy Schubert if (body_type != MKA_SAK_USE) {
33444bc52338SCy Schubert wpa_printf(MSG_INFO,
33454bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
33464bc52338SCy Schubert body_type);
33474bc52338SCy Schubert return -1;
33484bc52338SCy Schubert }
33494bc52338SCy Schubert
33504bc52338SCy Schubert /* Ideally DIST-SAK should be processed before
33514bc52338SCy Schubert * SAK-USE. Unfortunately IEEE Std 802.1X-2010,
33524bc52338SCy Schubert * 11.11.3 (Encoding MKPDUs) states SAK-USE(3)
33534bc52338SCy Schubert * must always be encoded before DIST-SAK(4).
33544bc52338SCy Schubert * Rather than redesigning mka_body_handler so
33554bc52338SCy Schubert * that it somehow processes DIST-SAK before
33564bc52338SCy Schubert * SAK-USE, just ignore SAK-USE failures if
33574bc52338SCy Schubert * DIST-SAK is also present in this MKPDU. */
3358c1d255d3SCy Schubert bad_sak_use = true;
33594bc52338SCy Schubert }
33605b9c547cSRui Paulo } else {
33615b9c547cSRui Paulo wpa_printf(MSG_ERROR,
33624bc52338SCy Schubert "KaY: The body type %d is not supported in this MKA version %d",
33635b9c547cSRui Paulo body_type, MKA_VERSION_ID);
33645b9c547cSRui Paulo }
33655b9c547cSRui Paulo }
33665b9c547cSRui Paulo
33674bc52338SCy Schubert if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) {
33684bc52338SCy Schubert wpa_printf(MSG_INFO,
33694bc52338SCy Schubert "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
33704bc52338SCy Schubert MKA_SAK_USE);
33714bc52338SCy Schubert if (!reset_participant_mi(participant))
3372*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not update MI");
33734bc52338SCy Schubert else
33744bc52338SCy Schubert wpa_printf(MSG_DEBUG,
33754bc52338SCy Schubert "KaY: Selected a new random MI: %s",
33764bc52338SCy Schubert mi_txt(participant->mi));
33774bc52338SCy Schubert return -1;
33784bc52338SCy Schubert }
33794bc52338SCy Schubert
33804bc52338SCy Schubert /* Detect missing parameter sets */
33814bc52338SCy Schubert peer = ieee802_1x_kay_get_live_peer(participant,
33824bc52338SCy Schubert participant->current_peer_id.mi);
33834bc52338SCy Schubert if (peer) {
33844bc52338SCy Schubert /* MKPDU is from live peer */
33854bc52338SCy Schubert if (!handled[MKA_SAK_USE]) {
33864bc52338SCy Schubert /* Once a live peer starts sending SAK-USE, it should be
33874bc52338SCy Schubert * sent every time. */
33884bc52338SCy Schubert if (peer->sak_used) {
33894bc52338SCy Schubert wpa_printf(MSG_INFO,
33904bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE");
33914bc52338SCy Schubert return -1;
33924bc52338SCy Schubert }
33934bc52338SCy Schubert
33944bc52338SCy Schubert /* Live peer is probably hung if it hasn't sent SAK-USE
33954bc52338SCy Schubert * after a reasonable number of MKPDUs. Drop the MKPDU,
33964bc52338SCy Schubert * which will eventually force an timeout. */
33974bc52338SCy Schubert if (++peer->missing_sak_use_count >
33984bc52338SCy Schubert MAX_MISSING_SAK_USE) {
33994bc52338SCy Schubert wpa_printf(MSG_INFO,
34004bc52338SCy Schubert "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE");
34014bc52338SCy Schubert return -1;
34024bc52338SCy Schubert }
34034bc52338SCy Schubert } else {
34044bc52338SCy Schubert peer->missing_sak_use_count = 0;
34054bc52338SCy Schubert
34064bc52338SCy Schubert /* Only update live peer watchdog after successful
34074bc52338SCy Schubert * decode of all parameter sets */
34084bc52338SCy Schubert peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
34094bc52338SCy Schubert }
34104bc52338SCy Schubert } else {
34114bc52338SCy Schubert /* MKPDU is from new or potential peer */
34124bc52338SCy Schubert peer = ieee802_1x_kay_get_peer(participant,
34134bc52338SCy Schubert participant->current_peer_id.mi);
34144bc52338SCy Schubert if (!peer) {
34154bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: No peer entry found");
34164bc52338SCy Schubert return -1;
34174bc52338SCy Schubert }
34184bc52338SCy Schubert
34194bc52338SCy Schubert /* Do not update potential peer watchdog. Per IEEE Std
34204bc52338SCy Schubert * 802.1X-2010, 9.4.3, potential peers need to show liveness by
34214bc52338SCy Schubert * including our MI/MN in their transmitted MKPDU (within
34224bc52338SCy Schubert * potential or live parameter sets). Whena potential peer does
34234bc52338SCy Schubert * include our MI/MN in an MKPDU, we respond by moving the peer
34244bc52338SCy Schubert * from 'potential_peers' to 'live_peers'. */
34254bc52338SCy Schubert }
34264bc52338SCy Schubert
3427c1d255d3SCy Schubert kay->active = true;
34285b9c547cSRui Paulo participant->retry_count = 0;
3429c1d255d3SCy Schubert participant->active = true;
34305b9c547cSRui Paulo
34315b9c547cSRui Paulo return 0;
34325b9c547cSRui Paulo }
34335b9c547cSRui Paulo
34345b9c547cSRui Paulo
34355b9c547cSRui Paulo
kay_l2_receive(void * ctx,const u8 * src_addr,const u8 * buf,size_t len)34365b9c547cSRui Paulo static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
34375b9c547cSRui Paulo size_t len)
34385b9c547cSRui Paulo {
34395b9c547cSRui Paulo struct ieee802_1x_kay *kay = ctx;
34405b9c547cSRui Paulo struct ieee8023_hdr *eth_hdr;
34415b9c547cSRui Paulo struct ieee802_1x_hdr *eapol_hdr;
34424bc52338SCy Schubert size_t calc_len;
34434bc52338SCy Schubert
34444bc52338SCy Schubert /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
34455b9c547cSRui Paulo
34465b9c547cSRui Paulo /* must contain at least ieee8023_hdr + ieee802_1x_hdr */
34475b9c547cSRui Paulo if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
34485b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
34495b9c547cSRui Paulo (unsigned long) len);
34505b9c547cSRui Paulo return;
34515b9c547cSRui Paulo }
34525b9c547cSRui Paulo
34535b9c547cSRui Paulo eth_hdr = (struct ieee8023_hdr *) buf;
34545b9c547cSRui Paulo eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
34554bc52338SCy Schubert calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
34564bc52338SCy Schubert be_to_host16(eapol_hdr->length);
34574bc52338SCy Schubert if (len < calc_len) {
34584bc52338SCy Schubert wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)",
34595b9c547cSRui Paulo (unsigned long) len,
34604bc52338SCy Schubert (unsigned long) calc_len,
34614bc52338SCy Schubert be_to_host16(eapol_hdr->length));
34625b9c547cSRui Paulo return;
34635b9c547cSRui Paulo }
34644bc52338SCy Schubert if (len > calc_len) {
34654bc52338SCy Schubert wpa_hexdump(MSG_DEBUG,
34664bc52338SCy Schubert "KaY: Ignore extra octets following the Packey Body field",
34674bc52338SCy Schubert &buf[calc_len], len - calc_len);
34684bc52338SCy Schubert len = calc_len;
34694bc52338SCy Schubert }
34705b9c547cSRui Paulo
34715b9c547cSRui Paulo if (eapol_hdr->version < EAPOL_VERSION) {
34725b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
34735b9c547cSRui Paulo eapol_hdr->version);
34745b9c547cSRui Paulo return;
34755b9c547cSRui Paulo }
3476780fb4a2SCy Schubert if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
34775b9c547cSRui Paulo eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
34784bc52338SCy Schubert return; /* ignore other EAPOL types silently here */
34795b9c547cSRui Paulo
34804bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len);
34815b9c547cSRui Paulo if (dl_list_empty(&kay->participant_list)) {
34824bc52338SCy Schubert wpa_printf(MSG_ERROR,
34834bc52338SCy Schubert "KaY: No MKA participant instance - ignore EAPOL-MKA");
34845b9c547cSRui Paulo return;
34855b9c547cSRui Paulo }
34865b9c547cSRui Paulo
34875b9c547cSRui Paulo ieee802_1x_kay_decode_mkpdu(kay, buf, len);
34885b9c547cSRui Paulo }
34895b9c547cSRui Paulo
34905b9c547cSRui Paulo
34915b9c547cSRui Paulo /**
34925b9c547cSRui Paulo * ieee802_1x_kay_init -
34935b9c547cSRui Paulo */
34945b9c547cSRui Paulo struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx * ctx,enum macsec_policy policy,bool macsec_replay_protect,u32 macsec_replay_window,u8 macsec_offload,u16 port,u8 priority,u32 macsec_csindex,const char * ifname,const u8 * addr)34955b9c547cSRui Paulo ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
3496c1d255d3SCy Schubert bool macsec_replay_protect, u32 macsec_replay_window,
3497*a90b9d01SCy Schubert u8 macsec_offload, u16 port, u8 priority,
3498*a90b9d01SCy Schubert u32 macsec_csindex, const char *ifname, const u8 *addr)
34995b9c547cSRui Paulo {
35005b9c547cSRui Paulo struct ieee802_1x_kay *kay;
35015b9c547cSRui Paulo
35024bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR
35034bc52338SCy Schubert " port=%u priority=%u",
35044bc52338SCy Schubert ifname, MAC2STR(addr), port, priority);
35055b9c547cSRui Paulo kay = os_zalloc(sizeof(*kay));
35065b9c547cSRui Paulo if (!kay) {
35075b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
350885732ac8SCy Schubert os_free(ctx);
35095b9c547cSRui Paulo return NULL;
35105b9c547cSRui Paulo }
35115b9c547cSRui Paulo
35125b9c547cSRui Paulo kay->ctx = ctx;
35135b9c547cSRui Paulo
3514c1d255d3SCy Schubert kay->enable = true;
3515c1d255d3SCy Schubert kay->active = false;
35165b9c547cSRui Paulo
3517c1d255d3SCy Schubert kay->authenticated = false;
3518c1d255d3SCy Schubert kay->secured = false;
3519c1d255d3SCy Schubert kay->failed = false;
35205b9c547cSRui Paulo kay->policy = policy;
35215b9c547cSRui Paulo
35225b9c547cSRui Paulo os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
35235b9c547cSRui Paulo os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
352485732ac8SCy Schubert kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
35254bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s",
35264bc52338SCy Schubert sci_txt(&kay->actor_sci));
352785732ac8SCy Schubert kay->actor_priority = priority;
35285b9c547cSRui Paulo
35295b9c547cSRui Paulo /* While actor acts as a key server, shall distribute sakey */
35305b9c547cSRui Paulo kay->dist_kn = 1;
35315b9c547cSRui Paulo kay->dist_an = 0;
35325b9c547cSRui Paulo kay->dist_time = 0;
35335b9c547cSRui Paulo
35345b9c547cSRui Paulo kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
3535*a90b9d01SCy Schubert kay->macsec_csindex = macsec_csindex;
35365b9c547cSRui Paulo kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
35375b9c547cSRui Paulo kay->mka_version = MKA_VERSION_ID;
35385b9c547cSRui Paulo
35395b9c547cSRui Paulo os_memcpy(kay->algo_agility, mka_algo_agility,
35405b9c547cSRui Paulo sizeof(kay->algo_agility));
35415b9c547cSRui Paulo
35425b9c547cSRui Paulo dl_list_init(&kay->participant_list);
35435b9c547cSRui Paulo
354485732ac8SCy Schubert if (policy != DO_NOT_SECURE &&
354585732ac8SCy Schubert secy_get_capability(kay, &kay->macsec_capable) < 0)
354685732ac8SCy Schubert goto error;
354785732ac8SCy Schubert
354885732ac8SCy Schubert if (policy == DO_NOT_SECURE ||
354985732ac8SCy Schubert kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
35505b9c547cSRui Paulo kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
3551c1d255d3SCy Schubert kay->macsec_desired = false;
3552c1d255d3SCy Schubert kay->macsec_protect = false;
3553c1d255d3SCy Schubert kay->macsec_encrypt = false;
35545b9c547cSRui Paulo kay->macsec_validate = Disabled;
3555c1d255d3SCy Schubert kay->macsec_replay_protect = false;
35565b9c547cSRui Paulo kay->macsec_replay_window = 0;
3557*a90b9d01SCy Schubert kay->macsec_offload = 0;
35585b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
35594bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME;
35605b9c547cSRui Paulo } else {
3561c1d255d3SCy Schubert kay->macsec_desired = true;
3562c1d255d3SCy Schubert kay->macsec_protect = true;
35634bc52338SCy Schubert if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF &&
35644bc52338SCy Schubert policy == SHOULD_ENCRYPT) {
3565c1d255d3SCy Schubert kay->macsec_encrypt = true;
35665b9c547cSRui Paulo kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
35674bc52338SCy Schubert } else { /* SHOULD_SECURE */
3568c1d255d3SCy Schubert kay->macsec_encrypt = false;
356985732ac8SCy Schubert kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
35705b9c547cSRui Paulo }
35714bc52338SCy Schubert kay->macsec_validate = Strict;
35724bc52338SCy Schubert kay->macsec_replay_protect = macsec_replay_protect;
35734bc52338SCy Schubert kay->macsec_replay_window = macsec_replay_window;
3574*a90b9d01SCy Schubert kay->macsec_offload = macsec_offload;
35754bc52338SCy Schubert kay->mka_hello_time = MKA_HELLO_TIME;
35764bc52338SCy Schubert }
35775b9c547cSRui Paulo
35785b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine created");
35795b9c547cSRui Paulo
35805b9c547cSRui Paulo /* Initialize the SecY must be prio to CP, as CP will control SecY */
358185732ac8SCy Schubert if (secy_init_macsec(kay) < 0) {
358285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
358385732ac8SCy Schubert goto error;
358485732ac8SCy Schubert }
35855b9c547cSRui Paulo
3586*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "KaY: SecY init MACsec done");
35875b9c547cSRui Paulo
35885b9c547cSRui Paulo /* init CP */
3589780fb4a2SCy Schubert kay->cp = ieee802_1x_cp_sm_init(kay);
359085732ac8SCy Schubert if (kay->cp == NULL)
359185732ac8SCy Schubert goto error;
35925b9c547cSRui Paulo
35935b9c547cSRui Paulo if (policy == DO_NOT_SECURE) {
35945b9c547cSRui Paulo ieee802_1x_cp_connect_authenticated(kay->cp);
35955b9c547cSRui Paulo ieee802_1x_cp_sm_step(kay->cp);
35965b9c547cSRui Paulo } else {
35975b9c547cSRui Paulo kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
35985b9c547cSRui Paulo kay_l2_receive, kay, 1);
35995b9c547cSRui Paulo if (kay->l2_mka == NULL) {
36005b9c547cSRui Paulo wpa_printf(MSG_WARNING,
36015b9c547cSRui Paulo "KaY: Failed to initialize L2 packet processing for MKA packet");
360285732ac8SCy Schubert goto error;
36035b9c547cSRui Paulo }
36045b9c547cSRui Paulo }
36055b9c547cSRui Paulo
36065b9c547cSRui Paulo return kay;
360785732ac8SCy Schubert
360885732ac8SCy Schubert error:
360985732ac8SCy Schubert ieee802_1x_kay_deinit(kay);
361085732ac8SCy Schubert return NULL;
36115b9c547cSRui Paulo }
36125b9c547cSRui Paulo
36135b9c547cSRui Paulo
36145b9c547cSRui Paulo /**
36155b9c547cSRui Paulo * ieee802_1x_kay_deinit -
36165b9c547cSRui Paulo */
36175b9c547cSRui Paulo void
ieee802_1x_kay_deinit(struct ieee802_1x_kay * kay)36185b9c547cSRui Paulo ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
36195b9c547cSRui Paulo {
36205b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
36215b9c547cSRui Paulo
36225b9c547cSRui Paulo if (!kay)
36235b9c547cSRui Paulo return;
36245b9c547cSRui Paulo
36255b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: state machine removed");
36265b9c547cSRui Paulo
36275b9c547cSRui Paulo while (!dl_list_empty(&kay->participant_list)) {
36285b9c547cSRui Paulo participant = dl_list_entry(kay->participant_list.next,
36295b9c547cSRui Paulo struct ieee802_1x_mka_participant,
36305b9c547cSRui Paulo list);
36315b9c547cSRui Paulo ieee802_1x_kay_delete_mka(kay, &participant->ckn);
36325b9c547cSRui Paulo }
36335b9c547cSRui Paulo
36345b9c547cSRui Paulo ieee802_1x_cp_sm_deinit(kay->cp);
36355b9c547cSRui Paulo secy_deinit_macsec(kay);
36365b9c547cSRui Paulo
36375b9c547cSRui Paulo if (kay->l2_mka) {
36385b9c547cSRui Paulo l2_packet_deinit(kay->l2_mka);
36395b9c547cSRui Paulo kay->l2_mka = NULL;
36405b9c547cSRui Paulo }
36415b9c547cSRui Paulo
36425b9c547cSRui Paulo os_free(kay->ctx);
36435b9c547cSRui Paulo os_free(kay);
36445b9c547cSRui Paulo }
36455b9c547cSRui Paulo
36465b9c547cSRui Paulo
mode_txt(enum mka_created_mode mode)36474bc52338SCy Schubert static const char * mode_txt(enum mka_created_mode mode)
36484bc52338SCy Schubert {
36494bc52338SCy Schubert switch (mode) {
36504bc52338SCy Schubert case PSK:
36514bc52338SCy Schubert return "PSK";
36524bc52338SCy Schubert case EAP_EXCHANGE:
36534bc52338SCy Schubert return "EAP";
36544bc52338SCy Schubert }
36554bc52338SCy Schubert
36564bc52338SCy Schubert return "?";
36574bc52338SCy Schubert }
36584bc52338SCy Schubert
36594bc52338SCy Schubert
36605b9c547cSRui Paulo /**
36615b9c547cSRui Paulo * ieee802_1x_kay_create_mka -
36625b9c547cSRui Paulo */
36635b9c547cSRui Paulo struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka(struct ieee802_1x_kay * kay,const struct mka_key_name * ckn,const struct mka_key * cak,u32 life,enum mka_created_mode mode,bool is_authenticator)366485732ac8SCy Schubert ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
366585732ac8SCy Schubert const struct mka_key_name *ckn,
366685732ac8SCy Schubert const struct mka_key *cak, u32 life,
3667c1d255d3SCy Schubert enum mka_created_mode mode, bool is_authenticator)
36685b9c547cSRui Paulo {
36695b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
36705b9c547cSRui Paulo unsigned int usecs;
36715b9c547cSRui Paulo
36724bc52338SCy Schubert wpa_printf(MSG_DEBUG,
36734bc52338SCy Schubert "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)",
36744bc52338SCy Schubert kay->if_name, mode_txt(mode), yes_no(is_authenticator));
36754bc52338SCy Schubert
36765b9c547cSRui Paulo if (!kay || !ckn || !cak) {
3677*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "KaY: CKN or CAK is null");
36785b9c547cSRui Paulo return NULL;
36795b9c547cSRui Paulo }
36805b9c547cSRui Paulo
36814bc52338SCy Schubert if (cak->len != 16 && cak->len != 32) {
36824bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u",
36834bc52338SCy Schubert (unsigned int) cak->len);
36845b9c547cSRui Paulo return NULL;
36855b9c547cSRui Paulo }
36865b9c547cSRui Paulo if (ckn->len > MAX_CKN_LEN) {
36874bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)");
36885b9c547cSRui Paulo return NULL;
36895b9c547cSRui Paulo }
36905b9c547cSRui Paulo if (!kay->enable) {
36915b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
36925b9c547cSRui Paulo return NULL;
36935b9c547cSRui Paulo }
36945b9c547cSRui Paulo
36955b9c547cSRui Paulo participant = os_zalloc(sizeof(*participant));
36965b9c547cSRui Paulo if (!participant) {
36975b9c547cSRui Paulo wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
36985b9c547cSRui Paulo return NULL;
36995b9c547cSRui Paulo }
37005b9c547cSRui Paulo
37015b9c547cSRui Paulo participant->ckn.len = ckn->len;
37025b9c547cSRui Paulo os_memcpy(participant->ckn.name, ckn->name, ckn->len);
37034bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name,
37044bc52338SCy Schubert participant->ckn.len);
37055b9c547cSRui Paulo participant->cak.len = cak->len;
37065b9c547cSRui Paulo os_memcpy(participant->cak.key, cak->key, cak->len);
37074bc52338SCy Schubert wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key,
37084bc52338SCy Schubert participant->cak.len);
37095b9c547cSRui Paulo if (life)
37105b9c547cSRui Paulo participant->cak_life = life + time(NULL);
37115b9c547cSRui Paulo
37125b9c547cSRui Paulo switch (mode) {
37135b9c547cSRui Paulo case EAP_EXCHANGE:
37145b9c547cSRui Paulo if (is_authenticator) {
3715c1d255d3SCy Schubert participant->is_obliged_key_server = true;
3716c1d255d3SCy Schubert participant->can_be_key_server = true;
3717c1d255d3SCy Schubert participant->is_key_server = true;
3718c1d255d3SCy Schubert participant->principal = true;
37195b9c547cSRui Paulo
37205b9c547cSRui Paulo os_memcpy(&kay->key_server_sci, &kay->actor_sci,
37215b9c547cSRui Paulo sizeof(kay->key_server_sci));
37225b9c547cSRui Paulo kay->key_server_priority = kay->actor_priority;
3723c1d255d3SCy Schubert participant->is_elected = true;
37245b9c547cSRui Paulo } else {
3725c1d255d3SCy Schubert participant->is_obliged_key_server = false;
3726c1d255d3SCy Schubert participant->can_be_key_server = false;
3727c1d255d3SCy Schubert participant->is_key_server = false;
3728c1d255d3SCy Schubert participant->is_elected = true;
37295b9c547cSRui Paulo }
37305b9c547cSRui Paulo break;
37315b9c547cSRui Paulo
37325b9c547cSRui Paulo default:
3733c1d255d3SCy Schubert participant->is_obliged_key_server = false;
3734c1d255d3SCy Schubert participant->can_be_key_server = true;
3735c1d255d3SCy Schubert participant->is_key_server = true;
3736c1d255d3SCy Schubert participant->is_elected = false;
37375b9c547cSRui Paulo break;
37385b9c547cSRui Paulo }
37395b9c547cSRui Paulo
3740c1d255d3SCy Schubert participant->cached = false;
37415b9c547cSRui Paulo
3742c1d255d3SCy Schubert participant->active = false;
3743c1d255d3SCy Schubert participant->participant = false;
3744c1d255d3SCy Schubert participant->retain = false;
37455b9c547cSRui Paulo participant->activate = DEFAULT;
37465b9c547cSRui Paulo
37475b9c547cSRui Paulo if (participant->is_key_server)
3748c1d255d3SCy Schubert participant->principal = true;
37495b9c547cSRui Paulo
37505b9c547cSRui Paulo dl_list_init(&participant->live_peers);
37515b9c547cSRui Paulo dl_list_init(&participant->potential_peers);
37525b9c547cSRui Paulo
37535b9c547cSRui Paulo participant->retry_count = 0;
37545b9c547cSRui Paulo participant->kay = kay;
37555b9c547cSRui Paulo
3756780fb4a2SCy Schubert if (!reset_participant_mi(participant))
37575b9c547cSRui Paulo goto fail;
37584bc52338SCy Schubert wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s",
37594bc52338SCy Schubert mi_txt(participant->mi));
37605b9c547cSRui Paulo
3761c1d255d3SCy Schubert participant->lrx = false;
3762c1d255d3SCy Schubert participant->ltx = false;
3763c1d255d3SCy Schubert participant->orx = false;
3764c1d255d3SCy Schubert participant->otx = false;
3765c1d255d3SCy Schubert participant->to_dist_sak = false;
3766c1d255d3SCy Schubert participant->to_use_sak = false;
3767c1d255d3SCy Schubert participant->new_sak = false;
37685b9c547cSRui Paulo dl_list_init(&participant->sak_list);
37695b9c547cSRui Paulo participant->new_key = NULL;
37705b9c547cSRui Paulo dl_list_init(&participant->rxsc_list);
377185732ac8SCy Schubert participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
37725b9c547cSRui Paulo secy_cp_control_protect_frames(kay, kay->macsec_protect);
37735b9c547cSRui Paulo secy_cp_control_replay(kay, kay->macsec_replay_protect,
37745b9c547cSRui Paulo kay->macsec_replay_window);
3775*a90b9d01SCy Schubert secy_cp_control_offload(kay, kay->macsec_offload);
37764bc52338SCy Schubert if (secy_create_transmit_sc(kay, participant->txsc))
37774bc52338SCy Schubert goto fail;
37785b9c547cSRui Paulo
37795b9c547cSRui Paulo /* to derive KEK from CAK and CKN */
37804bc52338SCy Schubert participant->kek.len = participant->cak.len;
37815b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
37824bc52338SCy Schubert participant->cak.len,
37835b9c547cSRui Paulo participant->ckn.name,
37845b9c547cSRui Paulo participant->ckn.len,
37854bc52338SCy Schubert participant->kek.key,
37864bc52338SCy Schubert participant->kek.len)) {
37874bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: KEK derivation failed");
37885b9c547cSRui Paulo goto fail;
37895b9c547cSRui Paulo }
37905b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
37915b9c547cSRui Paulo participant->kek.key, participant->kek.len);
37925b9c547cSRui Paulo
37935b9c547cSRui Paulo /* to derive ICK from CAK and CKN */
37944bc52338SCy Schubert participant->ick.len = participant->cak.len;
37955b9c547cSRui Paulo if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
37964bc52338SCy Schubert participant->cak.len,
37975b9c547cSRui Paulo participant->ckn.name,
37985b9c547cSRui Paulo participant->ckn.len,
37994bc52338SCy Schubert participant->ick.key,
38004bc52338SCy Schubert participant->ick.len)) {
38014bc52338SCy Schubert wpa_printf(MSG_ERROR, "KaY: ICK derivation failed");
38025b9c547cSRui Paulo goto fail;
38035b9c547cSRui Paulo }
38045b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
38055b9c547cSRui Paulo participant->ick.key, participant->ick.len);
38065b9c547cSRui Paulo
38075b9c547cSRui Paulo dl_list_add(&kay->participant_list, &participant->list);
38085b9c547cSRui Paulo
38094bc52338SCy Schubert usecs = os_random() % (kay->mka_hello_time * 1000);
38105b9c547cSRui Paulo eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
38115b9c547cSRui Paulo participant, NULL);
381285732ac8SCy Schubert
381385732ac8SCy Schubert /* Disable MKA lifetime for PSK mode.
381485732ac8SCy Schubert * The peer(s) can take a long time to come up, because we
381585732ac8SCy Schubert * create a "standby" MKA, and we need it to remain live until
381685732ac8SCy Schubert * some peer appears.
381785732ac8SCy Schubert */
381885732ac8SCy Schubert if (mode != PSK) {
38195b9c547cSRui Paulo participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
38205b9c547cSRui Paulo usecs / 1000000;
382185732ac8SCy Schubert }
382285732ac8SCy Schubert participant->mode = mode;
38235b9c547cSRui Paulo
38245b9c547cSRui Paulo return participant;
38255b9c547cSRui Paulo
38265b9c547cSRui Paulo fail:
38274bc52338SCy Schubert os_free(participant->txsc);
38285b9c547cSRui Paulo os_free(participant);
38295b9c547cSRui Paulo return NULL;
38305b9c547cSRui Paulo }
38315b9c547cSRui Paulo
38325b9c547cSRui Paulo
38335b9c547cSRui Paulo /**
38345b9c547cSRui Paulo * ieee802_1x_kay_delete_mka -
38355b9c547cSRui Paulo */
38365b9c547cSRui Paulo void
ieee802_1x_kay_delete_mka(struct ieee802_1x_kay * kay,struct mka_key_name * ckn)38375b9c547cSRui Paulo ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
38385b9c547cSRui Paulo {
38395b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
38405b9c547cSRui Paulo struct ieee802_1x_kay_peer *peer;
38415b9c547cSRui Paulo struct data_key *sak;
38425b9c547cSRui Paulo struct receive_sc *rxsc;
38435b9c547cSRui Paulo
38445b9c547cSRui Paulo if (!kay || !ckn)
38455b9c547cSRui Paulo return;
38465b9c547cSRui Paulo
38475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: participant removed");
38485b9c547cSRui Paulo
38495b9c547cSRui Paulo /* get the participant */
385085732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
38515b9c547cSRui Paulo if (!participant) {
38525b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
38535b9c547cSRui Paulo ckn->name, ckn->len);
38545b9c547cSRui Paulo return;
38555b9c547cSRui Paulo }
38565b9c547cSRui Paulo
3857780fb4a2SCy Schubert eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL);
38585b9c547cSRui Paulo dl_list_del(&participant->list);
38595b9c547cSRui Paulo
38605b9c547cSRui Paulo /* remove live peer */
38615b9c547cSRui Paulo while (!dl_list_empty(&participant->live_peers)) {
38625b9c547cSRui Paulo peer = dl_list_entry(participant->live_peers.next,
38635b9c547cSRui Paulo struct ieee802_1x_kay_peer, list);
38645b9c547cSRui Paulo dl_list_del(&peer->list);
38655b9c547cSRui Paulo os_free(peer);
38665b9c547cSRui Paulo }
38675b9c547cSRui Paulo
38685b9c547cSRui Paulo /* remove potential peer */
38695b9c547cSRui Paulo while (!dl_list_empty(&participant->potential_peers)) {
38705b9c547cSRui Paulo peer = dl_list_entry(participant->potential_peers.next,
38715b9c547cSRui Paulo struct ieee802_1x_kay_peer, list);
38725b9c547cSRui Paulo dl_list_del(&peer->list);
38735b9c547cSRui Paulo os_free(peer);
38745b9c547cSRui Paulo }
38755b9c547cSRui Paulo
38765b9c547cSRui Paulo /* remove sak */
38775b9c547cSRui Paulo while (!dl_list_empty(&participant->sak_list)) {
38785b9c547cSRui Paulo sak = dl_list_entry(participant->sak_list.next,
38795b9c547cSRui Paulo struct data_key, list);
38805b9c547cSRui Paulo dl_list_del(&sak->list);
388185732ac8SCy Schubert ieee802_1x_kay_deinit_data_key(sak);
38825b9c547cSRui Paulo }
38835b9c547cSRui Paulo while (!dl_list_empty(&participant->rxsc_list)) {
38845b9c547cSRui Paulo rxsc = dl_list_entry(participant->rxsc_list.next,
38855b9c547cSRui Paulo struct receive_sc, list);
38865b9c547cSRui Paulo ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
38875b9c547cSRui Paulo }
38885b9c547cSRui Paulo ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
38895b9c547cSRui Paulo
38905b9c547cSRui Paulo os_memset(&participant->cak, 0, sizeof(participant->cak));
38915b9c547cSRui Paulo os_memset(&participant->kek, 0, sizeof(participant->kek));
38925b9c547cSRui Paulo os_memset(&participant->ick, 0, sizeof(participant->ick));
38935b9c547cSRui Paulo os_free(participant);
38945b9c547cSRui Paulo }
38955b9c547cSRui Paulo
38965b9c547cSRui Paulo
38975b9c547cSRui Paulo /**
38985b9c547cSRui Paulo * ieee802_1x_kay_mka_participate -
38995b9c547cSRui Paulo */
ieee802_1x_kay_mka_participate(struct ieee802_1x_kay * kay,struct mka_key_name * ckn,bool status)39005b9c547cSRui Paulo void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
3901c1d255d3SCy Schubert struct mka_key_name *ckn, bool status)
39025b9c547cSRui Paulo {
39035b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
39045b9c547cSRui Paulo
39055b9c547cSRui Paulo if (!kay || !ckn)
39065b9c547cSRui Paulo return;
39075b9c547cSRui Paulo
390885732ac8SCy Schubert participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
39095b9c547cSRui Paulo if (!participant)
39105b9c547cSRui Paulo return;
39115b9c547cSRui Paulo
39125b9c547cSRui Paulo participant->active = status;
39135b9c547cSRui Paulo }
39145b9c547cSRui Paulo
39155b9c547cSRui Paulo
39165b9c547cSRui Paulo /**
39175b9c547cSRui Paulo * ieee802_1x_kay_new_sak -
39185b9c547cSRui Paulo */
39195b9c547cSRui Paulo int
ieee802_1x_kay_new_sak(struct ieee802_1x_kay * kay)39205b9c547cSRui Paulo ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
39215b9c547cSRui Paulo {
39225b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
39235b9c547cSRui Paulo
39245b9c547cSRui Paulo if (!kay)
39255b9c547cSRui Paulo return -1;
39265b9c547cSRui Paulo
39275b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay);
39285b9c547cSRui Paulo if (!participant)
39295b9c547cSRui Paulo return -1;
39305b9c547cSRui Paulo
3931c1d255d3SCy Schubert participant->new_sak = true;
39325b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
39335b9c547cSRui Paulo
39345b9c547cSRui Paulo return 0;
39355b9c547cSRui Paulo }
39365b9c547cSRui Paulo
39375b9c547cSRui Paulo
39385b9c547cSRui Paulo /**
39395b9c547cSRui Paulo * ieee802_1x_kay_change_cipher_suite -
39405b9c547cSRui Paulo */
39415b9c547cSRui Paulo int
ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay * kay,unsigned int cs_index)3942780fb4a2SCy Schubert ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
3943780fb4a2SCy Schubert unsigned int cs_index)
39445b9c547cSRui Paulo {
39455b9c547cSRui Paulo struct ieee802_1x_mka_participant *participant;
394685732ac8SCy Schubert enum macsec_cap secy_cap;
39475b9c547cSRui Paulo
39485b9c547cSRui Paulo if (!kay)
39495b9c547cSRui Paulo return -1;
39505b9c547cSRui Paulo
3951780fb4a2SCy Schubert if (cs_index >= CS_TABLE_SIZE) {
39525b9c547cSRui Paulo wpa_printf(MSG_ERROR,
39535b9c547cSRui Paulo "KaY: Configured cipher suite index is out of range");
39545b9c547cSRui Paulo return -1;
39555b9c547cSRui Paulo }
39565b9c547cSRui Paulo if (kay->macsec_csindex == cs_index)
39575b9c547cSRui Paulo return -2;
39585b9c547cSRui Paulo
39595b9c547cSRui Paulo if (cs_index == 0)
3960c1d255d3SCy Schubert kay->macsec_desired = false;
39615b9c547cSRui Paulo
39625b9c547cSRui Paulo kay->macsec_csindex = cs_index;
39635b9c547cSRui Paulo kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
39645b9c547cSRui Paulo
396585732ac8SCy Schubert if (secy_get_capability(kay, &secy_cap) < 0)
396685732ac8SCy Schubert return -3;
396785732ac8SCy Schubert
396885732ac8SCy Schubert if (kay->macsec_capable > secy_cap)
396985732ac8SCy Schubert kay->macsec_capable = secy_cap;
397085732ac8SCy Schubert
39715b9c547cSRui Paulo participant = ieee802_1x_kay_get_principal_participant(kay);
39725b9c547cSRui Paulo if (participant) {
39735b9c547cSRui Paulo wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
3974c1d255d3SCy Schubert participant->new_sak = true;
39755b9c547cSRui Paulo }
39765b9c547cSRui Paulo
39775b9c547cSRui Paulo return 0;
39785b9c547cSRui Paulo }
397985732ac8SCy Schubert
398085732ac8SCy Schubert
398185732ac8SCy Schubert #ifdef CONFIG_CTRL_IFACE
39824bc52338SCy Schubert
398385732ac8SCy Schubert /**
398485732ac8SCy Schubert * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
398585732ac8SCy Schubert * @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
398685732ac8SCy Schubert * @buf: Buffer for status information
398785732ac8SCy Schubert * @buflen: Maximum buffer length
398885732ac8SCy Schubert * @verbose: Whether to include verbose status information
398985732ac8SCy Schubert * Returns: Number of bytes written to buf.
399085732ac8SCy Schubert *
39914bc52338SCy Schubert * Query KaY status information. This function fills in a text area with current
399285732ac8SCy Schubert * status information. If the buffer (buf) is not large enough, status
399385732ac8SCy Schubert * information will be truncated to fit the buffer.
399485732ac8SCy Schubert */
ieee802_1x_kay_get_status(struct ieee802_1x_kay * kay,char * buf,size_t buflen)399585732ac8SCy Schubert int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
399685732ac8SCy Schubert size_t buflen)
399785732ac8SCy Schubert {
39984bc52338SCy Schubert char *pos, *end;
39994bc52338SCy Schubert int res, count;
40004bc52338SCy Schubert struct ieee802_1x_mka_participant *p;
400185732ac8SCy Schubert
400285732ac8SCy Schubert if (!kay)
400385732ac8SCy Schubert return 0;
400485732ac8SCy Schubert
40054bc52338SCy Schubert pos = buf;
40064bc52338SCy Schubert end = buf + buflen;
40074bc52338SCy Schubert
40084bc52338SCy Schubert res = os_snprintf(pos, end - pos,
400985732ac8SCy Schubert "PAE KaY status=%s\n"
401085732ac8SCy Schubert "Authenticated=%s\n"
401185732ac8SCy Schubert "Secured=%s\n"
401285732ac8SCy Schubert "Failed=%s\n"
401385732ac8SCy Schubert "Actor Priority=%u\n"
401485732ac8SCy Schubert "Key Server Priority=%u\n"
401585732ac8SCy Schubert "Is Key Server=%s\n"
401685732ac8SCy Schubert "Number of Keys Distributed=%u\n"
40174bc52338SCy Schubert "Number of Keys Received=%u\n"
40184bc52338SCy Schubert "MKA Hello Time=%u\n",
401985732ac8SCy Schubert kay->active ? "Active" : "Not-Active",
402085732ac8SCy Schubert kay->authenticated ? "Yes" : "No",
402185732ac8SCy Schubert kay->secured ? "Yes" : "No",
402285732ac8SCy Schubert kay->failed ? "Yes" : "No",
402385732ac8SCy Schubert kay->actor_priority,
402485732ac8SCy Schubert kay->key_server_priority,
402585732ac8SCy Schubert kay->is_key_server ? "Yes" : "No",
402685732ac8SCy Schubert kay->dist_kn - 1,
40274bc52338SCy Schubert kay->rcvd_keys,
40284bc52338SCy Schubert kay->mka_hello_time);
40294bc52338SCy Schubert if (os_snprintf_error(buflen, res))
40304bc52338SCy Schubert return 0;
40314bc52338SCy Schubert pos += res;
40324bc52338SCy Schubert
40334bc52338SCy Schubert res = os_snprintf(pos, end - pos,
40344bc52338SCy Schubert "actor_sci=%s\n", sci_txt(&kay->actor_sci));
40354bc52338SCy Schubert if (os_snprintf_error(buflen, res))
40364bc52338SCy Schubert return end - pos;
40374bc52338SCy Schubert pos += res;
40384bc52338SCy Schubert
40394bc52338SCy Schubert res = os_snprintf(pos, end - pos,
40404bc52338SCy Schubert "key_server_sci=%s\n", sci_txt(&kay->key_server_sci));
40414bc52338SCy Schubert if (os_snprintf_error(buflen, res))
40424bc52338SCy Schubert return end - pos;
40434bc52338SCy Schubert pos += res;
40444bc52338SCy Schubert
40454bc52338SCy Schubert count = 0;
40464bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list,
40474bc52338SCy Schubert struct ieee802_1x_mka_participant, list) {
40484bc52338SCy Schubert char *pos2 = pos;
40494bc52338SCy Schubert
40504bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=",
40514bc52338SCy Schubert count);
40524bc52338SCy Schubert if (os_snprintf_error(buflen, res))
40534bc52338SCy Schubert return end - pos;
40544bc52338SCy Schubert pos2 += res;
40554bc52338SCy Schubert count++;
40564bc52338SCy Schubert
40574bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
40584bc52338SCy Schubert p->ckn.len);
40594bc52338SCy Schubert
40604bc52338SCy Schubert res = os_snprintf(pos2, end - pos2,
40614bc52338SCy Schubert "\nmi=%s\n"
40624bc52338SCy Schubert "mn=%u\n"
40634bc52338SCy Schubert "active=%s\n"
40644bc52338SCy Schubert "participant=%s\n"
40654bc52338SCy Schubert "retain=%s\n"
40664bc52338SCy Schubert "live_peers=%u\n"
40674bc52338SCy Schubert "potential_peers=%u\n"
40684bc52338SCy Schubert "is_key_server=%s\n"
40694bc52338SCy Schubert "is_elected=%s\n",
40704bc52338SCy Schubert mi_txt(p->mi), p->mn,
40714bc52338SCy Schubert yes_no(p->active),
40724bc52338SCy Schubert yes_no(p->participant),
40734bc52338SCy Schubert yes_no(p->retain),
40744bc52338SCy Schubert dl_list_len(&p->live_peers),
40754bc52338SCy Schubert dl_list_len(&p->potential_peers),
40764bc52338SCy Schubert yes_no(p->is_key_server),
40774bc52338SCy Schubert yes_no(p->is_elected));
40784bc52338SCy Schubert if (os_snprintf_error(buflen, res))
40794bc52338SCy Schubert return end - pos;
40804bc52338SCy Schubert pos2 += res;
40814bc52338SCy Schubert pos = pos2;
40824bc52338SCy Schubert }
40834bc52338SCy Schubert
40844bc52338SCy Schubert return pos - buf;
40854bc52338SCy Schubert }
40864bc52338SCy Schubert
40874bc52338SCy Schubert
true_false(bool val)4088c1d255d3SCy Schubert static const char * true_false(bool val)
40894bc52338SCy Schubert {
40904bc52338SCy Schubert return val ? "true" : "false";
40914bc52338SCy Schubert }
40924bc52338SCy Schubert
40934bc52338SCy Schubert
activate_control_txt(enum activate_ctrl activate)40944bc52338SCy Schubert static const char * activate_control_txt(enum activate_ctrl activate)
40954bc52338SCy Schubert {
40964bc52338SCy Schubert switch (activate) {
40974bc52338SCy Schubert case DEFAULT:
40984bc52338SCy Schubert return "default";
40994bc52338SCy Schubert case DISABLED:
41004bc52338SCy Schubert return "disabled";
41014bc52338SCy Schubert case ON_OPER_UP:
41024bc52338SCy Schubert return "onOperUp";
41034bc52338SCy Schubert case ALWAYS:
41044bc52338SCy Schubert return "always";
41054bc52338SCy Schubert }
41064bc52338SCy Schubert
41074bc52338SCy Schubert return "?";
41084bc52338SCy Schubert }
41094bc52338SCy Schubert
41104bc52338SCy Schubert
mka_mib_peer(struct dl_list * peers,bool live,char * buf,char * end)4111c1d255d3SCy Schubert static char * mka_mib_peer(struct dl_list *peers, bool live, char *buf,
41124bc52338SCy Schubert char *end)
41134bc52338SCy Schubert {
41144bc52338SCy Schubert char *pos = buf;
41154bc52338SCy Schubert struct ieee802_1x_kay_peer *p;
41164bc52338SCy Schubert int res;
41174bc52338SCy Schubert
41184bc52338SCy Schubert dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) {
41194bc52338SCy Schubert res = os_snprintf(pos, end - pos,
41204bc52338SCy Schubert "ieee8021XKayMkaPeerListMI=%s\n"
41214bc52338SCy Schubert "ieee8021XKayMkaPeerListMN=%u\n"
41224bc52338SCy Schubert "ieee8021XKayMkaPeerListType=%u\n"
41234bc52338SCy Schubert "ieee8021XKayMkaPeerListSCI=%s\n",
41244bc52338SCy Schubert mi_txt(p->mi),
41254bc52338SCy Schubert p->mn,
41264bc52338SCy Schubert live ? 1 : 2,
41274bc52338SCy Schubert sci_txt(&p->sci));
41284bc52338SCy Schubert if (os_snprintf_error(end - pos, res))
41294bc52338SCy Schubert return pos;
41304bc52338SCy Schubert pos += res;
41314bc52338SCy Schubert }
41324bc52338SCy Schubert
41334bc52338SCy Schubert return pos;
41344bc52338SCy Schubert }
41354bc52338SCy Schubert
41364bc52338SCy Schubert
ieee802_1x_kay_get_mib(struct ieee802_1x_kay * kay,char * buf,size_t buflen)41374bc52338SCy Schubert int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
41384bc52338SCy Schubert size_t buflen)
41394bc52338SCy Schubert {
41404bc52338SCy Schubert char *pos, *end;
41414bc52338SCy Schubert int res;
41424bc52338SCy Schubert struct ieee802_1x_mka_participant *p;
41434bc52338SCy Schubert
41444bc52338SCy Schubert if (!kay)
414585732ac8SCy Schubert return 0;
414685732ac8SCy Schubert
41474bc52338SCy Schubert pos = buf;
41484bc52338SCy Schubert end = buf + buflen;
41494bc52338SCy Schubert
41504bc52338SCy Schubert dl_list_for_each(p, &kay->participant_list,
41514bc52338SCy Schubert struct ieee802_1x_mka_participant, list) {
41524bc52338SCy Schubert char *pos2 = pos;
41534bc52338SCy Schubert
41544bc52338SCy Schubert res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN=");
41554bc52338SCy Schubert if (os_snprintf_error(buflen, res))
41564bc52338SCy Schubert return end - pos;
41574bc52338SCy Schubert pos2 += res;
41584bc52338SCy Schubert
41594bc52338SCy Schubert pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
41604bc52338SCy Schubert p->ckn.len);
41614bc52338SCy Schubert
41624bc52338SCy Schubert res = os_snprintf(pos2, end - pos2,
41634bc52338SCy Schubert "\nieee8021XKayMkaPartCached=%s\n"
41644bc52338SCy Schubert "ieee8021XKayMkaPartActive=%s\n"
41654bc52338SCy Schubert "ieee8021XKayMkaPartRetain=%s\n"
41664bc52338SCy Schubert "ieee8021XKayMkaPartActivateControl=%s\n"
41674bc52338SCy Schubert "ieee8021XKayMkaPartPrincipal=%s\n",
41684bc52338SCy Schubert true_false(p->cached),
41694bc52338SCy Schubert true_false(p->active),
41704bc52338SCy Schubert true_false(p->retain),
41714bc52338SCy Schubert activate_control_txt(p->activate),
41724bc52338SCy Schubert true_false(p->principal));
41734bc52338SCy Schubert if (os_snprintf_error(buflen, res))
41744bc52338SCy Schubert return end - pos;
41754bc52338SCy Schubert pos2 += res;
41764bc52338SCy Schubert pos = pos2;
41774bc52338SCy Schubert
4178c1d255d3SCy Schubert pos = mka_mib_peer(&p->live_peers, true, pos, end);
4179c1d255d3SCy Schubert pos = mka_mib_peer(&p->potential_peers, false, pos, end);
418085732ac8SCy Schubert }
41814bc52338SCy Schubert
41824bc52338SCy Schubert return pos - buf;
41834bc52338SCy Schubert }
41844bc52338SCy Schubert
418585732ac8SCy Schubert #endif /* CONFIG_CTRL_IFACE */
4186