xref: /freebsd/contrib/wpa/src/pae/ieee802_1x_cp.c (revision 85732ac8bccbc0adcf5a261ea1ffec8ca7b3a92d)
15b9c547cSRui Paulo /*
25b9c547cSRui Paulo  * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
35b9c547cSRui Paulo  * Copyright (c) 2013-2014, 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 "utils/includes.h"
105b9c547cSRui Paulo 
115b9c547cSRui Paulo #include "utils/common.h"
125b9c547cSRui Paulo #include "utils/eloop.h"
135b9c547cSRui Paulo #include "common/defs.h"
145b9c547cSRui Paulo #include "common/ieee802_1x_defs.h"
155b9c547cSRui Paulo #include "utils/state_machine.h"
165b9c547cSRui Paulo #include "ieee802_1x_kay.h"
175b9c547cSRui Paulo #include "ieee802_1x_secy_ops.h"
185b9c547cSRui Paulo #include "pae/ieee802_1x_cp.h"
195b9c547cSRui Paulo 
205b9c547cSRui Paulo #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
215b9c547cSRui Paulo #define STATE_MACHINE_DEBUG_PREFIX "CP"
225b9c547cSRui Paulo 
23780fb4a2SCy Schubert static u64 default_cs_id = CS_ID_GCM_AES_128;
245b9c547cSRui Paulo 
255b9c547cSRui Paulo /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
265b9c547cSRui Paulo enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
275b9c547cSRui Paulo 
285b9c547cSRui Paulo struct ieee802_1x_cp_sm {
295b9c547cSRui Paulo 	enum cp_states {
305b9c547cSRui Paulo 		CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
315b9c547cSRui Paulo 		CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
325b9c547cSRui Paulo 		CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
335b9c547cSRui Paulo 	} CP_state;
345b9c547cSRui Paulo 	Boolean changed;
355b9c547cSRui Paulo 
365b9c547cSRui Paulo 	/* CP -> Client */
375b9c547cSRui Paulo 	Boolean port_valid;
385b9c547cSRui Paulo 
395b9c547cSRui Paulo 	/* Logon -> CP */
405b9c547cSRui Paulo 	enum connect_type connect;
415b9c547cSRui Paulo 	u8 *authorization_data;
425b9c547cSRui Paulo 
435b9c547cSRui Paulo 	/* KaY -> CP */
445b9c547cSRui Paulo 	Boolean chgd_server; /* clear by CP */
455b9c547cSRui Paulo 	Boolean elected_self;
465b9c547cSRui Paulo 	u8 *authorization_data1;
475b9c547cSRui Paulo 	enum confidentiality_offset cipher_offset;
48780fb4a2SCy Schubert 	u64 cipher_suite;
495b9c547cSRui Paulo 	Boolean new_sak; /* clear by CP */
505b9c547cSRui Paulo 	struct ieee802_1x_mka_ki distributed_ki;
515b9c547cSRui Paulo 	u8 distributed_an;
525b9c547cSRui Paulo 	Boolean using_receive_sas;
535b9c547cSRui Paulo 	Boolean all_receiving;
545b9c547cSRui Paulo 	Boolean server_transmitting;
555b9c547cSRui Paulo 	Boolean using_transmit_sa;
565b9c547cSRui Paulo 
575b9c547cSRui Paulo 	/* CP -> KaY */
585b9c547cSRui Paulo 	struct ieee802_1x_mka_ki *lki;
595b9c547cSRui Paulo 	u8 lan;
605b9c547cSRui Paulo 	Boolean ltx;
615b9c547cSRui Paulo 	Boolean lrx;
625b9c547cSRui Paulo 	struct ieee802_1x_mka_ki *oki;
635b9c547cSRui Paulo 	u8 oan;
645b9c547cSRui Paulo 	Boolean otx;
655b9c547cSRui Paulo 	Boolean orx;
665b9c547cSRui Paulo 
675b9c547cSRui Paulo 	/* CP -> SecY */
685b9c547cSRui Paulo 	Boolean protect_frames;
695b9c547cSRui Paulo 	enum validate_frames validate_frames;
705b9c547cSRui Paulo 
715b9c547cSRui Paulo 	Boolean replay_protect;
725b9c547cSRui Paulo 	u32 replay_window;
735b9c547cSRui Paulo 
74780fb4a2SCy Schubert 	u64 current_cipher_suite;
755b9c547cSRui Paulo 	enum confidentiality_offset confidentiality_offset;
765b9c547cSRui Paulo 	Boolean controlled_port_enabled;
775b9c547cSRui Paulo 
785b9c547cSRui Paulo 	/* SecY -> CP */
795b9c547cSRui Paulo 	Boolean port_enabled; /* SecY->CP */
805b9c547cSRui Paulo 
815b9c547cSRui Paulo 	/* private */
825b9c547cSRui Paulo 	u32 transmit_when;
835b9c547cSRui Paulo 	u32 transmit_delay;
845b9c547cSRui Paulo 	u32 retire_when;
855b9c547cSRui Paulo 	u32 retire_delay;
865b9c547cSRui Paulo 
875b9c547cSRui Paulo 	/* not defined IEEE Std 802.1X-2010 */
885b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
895b9c547cSRui Paulo };
905b9c547cSRui Paulo 
915b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
925b9c547cSRui Paulo 					      void *timeout_ctx);
935b9c547cSRui Paulo static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
945b9c547cSRui Paulo 						void *timeout_ctx);
955b9c547cSRui Paulo 
965b9c547cSRui Paulo 
975b9c547cSRui Paulo static int changed_cipher(struct ieee802_1x_cp_sm *sm)
985b9c547cSRui Paulo {
995b9c547cSRui Paulo 	return sm->confidentiality_offset != sm->cipher_offset ||
100780fb4a2SCy Schubert 		sm->current_cipher_suite != sm->cipher_suite;
1015b9c547cSRui Paulo }
1025b9c547cSRui Paulo 
1035b9c547cSRui Paulo 
1045b9c547cSRui Paulo static int changed_connect(struct ieee802_1x_cp_sm *sm)
1055b9c547cSRui Paulo {
1065b9c547cSRui Paulo 	return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
1075b9c547cSRui Paulo }
1085b9c547cSRui Paulo 
1095b9c547cSRui Paulo 
1105b9c547cSRui Paulo SM_STATE(CP, INIT)
1115b9c547cSRui Paulo {
1125b9c547cSRui Paulo 	SM_ENTRY(CP, INIT);
1135b9c547cSRui Paulo 
1145b9c547cSRui Paulo 	sm->controlled_port_enabled = FALSE;
1155b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1165b9c547cSRui Paulo 
1175b9c547cSRui Paulo 	sm->port_valid = FALSE;
1185b9c547cSRui Paulo 
1195b9c547cSRui Paulo 	os_free(sm->lki);
1205b9c547cSRui Paulo 	sm->lki = NULL;
1215b9c547cSRui Paulo 	sm->ltx = FALSE;
1225b9c547cSRui Paulo 	sm->lrx = FALSE;
1235b9c547cSRui Paulo 
1245b9c547cSRui Paulo 	os_free(sm->oki);
1255b9c547cSRui Paulo 	sm->oki = NULL;
1265b9c547cSRui Paulo 	sm->otx = FALSE;
1275b9c547cSRui Paulo 	sm->orx = FALSE;
1285b9c547cSRui Paulo 
1295b9c547cSRui Paulo 	sm->port_enabled = TRUE;
1305b9c547cSRui Paulo 	sm->chgd_server = FALSE;
1315b9c547cSRui Paulo }
1325b9c547cSRui Paulo 
1335b9c547cSRui Paulo 
1345b9c547cSRui Paulo SM_STATE(CP, CHANGE)
1355b9c547cSRui Paulo {
1365b9c547cSRui Paulo 	SM_ENTRY(CP, CHANGE);
1375b9c547cSRui Paulo 
1385b9c547cSRui Paulo 	sm->port_valid = FALSE;
1395b9c547cSRui Paulo 	sm->controlled_port_enabled = FALSE;
1405b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1415b9c547cSRui Paulo 
1425b9c547cSRui Paulo 	if (sm->lki)
1435b9c547cSRui Paulo 		ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
1445b9c547cSRui Paulo 	if (sm->oki)
1455b9c547cSRui Paulo 		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
1465b9c547cSRui Paulo }
1475b9c547cSRui Paulo 
1485b9c547cSRui Paulo 
1495b9c547cSRui Paulo SM_STATE(CP, ALLOWED)
1505b9c547cSRui Paulo {
1515b9c547cSRui Paulo 	SM_ENTRY(CP, ALLOWED);
1525b9c547cSRui Paulo 
1535b9c547cSRui Paulo 	sm->protect_frames = FALSE;
1545b9c547cSRui Paulo 	sm->replay_protect = FALSE;
1555b9c547cSRui Paulo 	sm->validate_frames = Checked;
1565b9c547cSRui Paulo 
1575b9c547cSRui Paulo 	sm->port_valid = FALSE;
1585b9c547cSRui Paulo 	sm->controlled_port_enabled = TRUE;
1595b9c547cSRui Paulo 
1605b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1615b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
162*85732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
1635b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
1645b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
1655b9c547cSRui Paulo }
1665b9c547cSRui Paulo 
1675b9c547cSRui Paulo 
1685b9c547cSRui Paulo SM_STATE(CP, AUTHENTICATED)
1695b9c547cSRui Paulo {
1705b9c547cSRui Paulo 	SM_ENTRY(CP, AUTHENTICATED);
1715b9c547cSRui Paulo 
1725b9c547cSRui Paulo 	sm->protect_frames = FALSE;
1735b9c547cSRui Paulo 	sm->replay_protect = FALSE;
1745b9c547cSRui Paulo 	sm->validate_frames = Checked;
1755b9c547cSRui Paulo 
1765b9c547cSRui Paulo 	sm->port_valid = FALSE;
1775b9c547cSRui Paulo 	sm->controlled_port_enabled = TRUE;
1785b9c547cSRui Paulo 
1795b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1805b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
181*85732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
1825b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
1835b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
1845b9c547cSRui Paulo }
1855b9c547cSRui Paulo 
1865b9c547cSRui Paulo 
1875b9c547cSRui Paulo SM_STATE(CP, SECURED)
1885b9c547cSRui Paulo {
1895b9c547cSRui Paulo 	SM_ENTRY(CP, SECURED);
1905b9c547cSRui Paulo 
1915b9c547cSRui Paulo 	sm->chgd_server = FALSE;
1925b9c547cSRui Paulo 
193780fb4a2SCy Schubert 	sm->protect_frames = sm->kay->macsec_protect;
194780fb4a2SCy Schubert 	sm->replay_protect = sm->kay->macsec_replay_protect;
195780fb4a2SCy Schubert 	sm->validate_frames = sm->kay->macsec_validate;
1965b9c547cSRui Paulo 
197780fb4a2SCy Schubert 	/* NOTE: now no other than default cipher suite (AES-GCM-128) */
198780fb4a2SCy Schubert 	sm->current_cipher_suite = sm->cipher_suite;
199780fb4a2SCy Schubert 	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
2005b9c547cSRui Paulo 
2015b9c547cSRui Paulo 	sm->confidentiality_offset = sm->cipher_offset;
2025b9c547cSRui Paulo 
2035b9c547cSRui Paulo 	sm->port_valid = TRUE;
2045b9c547cSRui Paulo 
2055b9c547cSRui Paulo 	secy_cp_control_confidentiality_offset(sm->kay,
2065b9c547cSRui Paulo 					       sm->confidentiality_offset);
2075b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
208*85732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
2095b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
2105b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
2115b9c547cSRui Paulo }
2125b9c547cSRui Paulo 
2135b9c547cSRui Paulo 
2145b9c547cSRui Paulo SM_STATE(CP, RECEIVE)
2155b9c547cSRui Paulo {
2165b9c547cSRui Paulo 	SM_ENTRY(CP, RECEIVE);
2175b9c547cSRui Paulo 	/* RECEIVE state machine not keep with Figure 12-2 in
2185b9c547cSRui Paulo 	 * IEEE Std 802.1X-2010 */
2195b9c547cSRui Paulo 	sm->oki = sm->lki;
2205b9c547cSRui Paulo 	sm->oan = sm->lan;
2215b9c547cSRui Paulo 	sm->otx = sm->ltx;
2225b9c547cSRui Paulo 	sm->orx = sm->lrx;
2235b9c547cSRui Paulo 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
2245b9c547cSRui Paulo 				       sm->otx, sm->orx);
2255b9c547cSRui Paulo 
2265b9c547cSRui Paulo 	sm->lki = os_malloc(sizeof(*sm->lki));
2275b9c547cSRui Paulo 	if (!sm->lki) {
2285b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
2295b9c547cSRui Paulo 		return;
2305b9c547cSRui Paulo 	}
2315b9c547cSRui Paulo 	os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
2325b9c547cSRui Paulo 	sm->lan = sm->distributed_an;
2335b9c547cSRui Paulo 	sm->ltx = FALSE;
2345b9c547cSRui Paulo 	sm->lrx = FALSE;
2355b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2365b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2375b9c547cSRui Paulo 	ieee802_1x_kay_create_sas(sm->kay, sm->lki);
2385b9c547cSRui Paulo 	ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
2395b9c547cSRui Paulo 	sm->new_sak = FALSE;
2405b9c547cSRui Paulo 	sm->all_receiving = FALSE;
2415b9c547cSRui Paulo }
2425b9c547cSRui Paulo 
2435b9c547cSRui Paulo 
2445b9c547cSRui Paulo SM_STATE(CP, RECEIVING)
2455b9c547cSRui Paulo {
2465b9c547cSRui Paulo 	SM_ENTRY(CP, RECEIVING);
2475b9c547cSRui Paulo 
2485b9c547cSRui Paulo 	sm->lrx = TRUE;
2495b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2505b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2515b9c547cSRui Paulo 	sm->transmit_when = sm->transmit_delay;
2525b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
2535b9c547cSRui Paulo 	eloop_register_timeout(sm->transmit_when / 1000, 0,
2545b9c547cSRui Paulo 			       ieee802_1x_cp_transmit_when_timeout, sm, NULL);
2555b9c547cSRui Paulo 	/* the electedSelf have been set before CP entering to RECEIVING
2565b9c547cSRui Paulo 	 * but the CP will transmit from RECEIVING to READY under
2575b9c547cSRui Paulo 	 * the !electedSelf when KaY is not key server */
2585b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(sm);
2595b9c547cSRui Paulo 	sm->using_receive_sas = FALSE;
2605b9c547cSRui Paulo 	sm->server_transmitting = FALSE;
2615b9c547cSRui Paulo }
2625b9c547cSRui Paulo 
2635b9c547cSRui Paulo 
2645b9c547cSRui Paulo SM_STATE(CP, READY)
2655b9c547cSRui Paulo {
2665b9c547cSRui Paulo 	SM_ENTRY(CP, READY);
2675b9c547cSRui Paulo 
2685b9c547cSRui Paulo 	ieee802_1x_kay_enable_new_info(sm->kay);
2695b9c547cSRui Paulo }
2705b9c547cSRui Paulo 
2715b9c547cSRui Paulo 
2725b9c547cSRui Paulo SM_STATE(CP, TRANSMIT)
2735b9c547cSRui Paulo {
2745b9c547cSRui Paulo 	SM_ENTRY(CP, TRANSMIT);
2755b9c547cSRui Paulo 
2765b9c547cSRui Paulo 	sm->controlled_port_enabled = TRUE;
2775b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
2785b9c547cSRui Paulo 	sm->ltx = TRUE;
2795b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2805b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2815b9c547cSRui Paulo 	ieee802_1x_kay_enable_tx_sas(sm->kay,  sm->lki);
2825b9c547cSRui Paulo 	sm->all_receiving = FALSE;
2835b9c547cSRui Paulo 	sm->server_transmitting = FALSE;
2845b9c547cSRui Paulo }
2855b9c547cSRui Paulo 
2865b9c547cSRui Paulo 
2875b9c547cSRui Paulo SM_STATE(CP, TRANSMITTING)
2885b9c547cSRui Paulo {
2895b9c547cSRui Paulo 	SM_ENTRY(CP, TRANSMITTING);
2905b9c547cSRui Paulo 	sm->retire_when = sm->orx ? sm->retire_delay : 0;
2915b9c547cSRui Paulo 	sm->otx = FALSE;
2925b9c547cSRui Paulo 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
2935b9c547cSRui Paulo 				       sm->otx, sm->orx);
2945b9c547cSRui Paulo 	ieee802_1x_kay_enable_new_info(sm->kay);
2955b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
2965b9c547cSRui Paulo 	eloop_register_timeout(sm->retire_when / 1000, 0,
2975b9c547cSRui Paulo 			       ieee802_1x_cp_retire_when_timeout, sm, NULL);
2985b9c547cSRui Paulo 	sm->using_transmit_sa = FALSE;
2995b9c547cSRui Paulo }
3005b9c547cSRui Paulo 
3015b9c547cSRui Paulo 
3025b9c547cSRui Paulo SM_STATE(CP, ABANDON)
3035b9c547cSRui Paulo {
3045b9c547cSRui Paulo 	SM_ENTRY(CP, ABANDON);
3055b9c547cSRui Paulo 	sm->lrx = FALSE;
3065b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
3075b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
3085b9c547cSRui Paulo 	ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
3095b9c547cSRui Paulo 
3105b9c547cSRui Paulo 	os_free(sm->lki);
3115b9c547cSRui Paulo 	sm->lki = NULL;
3125b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
3135b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
3145b9c547cSRui Paulo 	sm->new_sak = FALSE;
3155b9c547cSRui Paulo }
3165b9c547cSRui Paulo 
3175b9c547cSRui Paulo 
3185b9c547cSRui Paulo SM_STATE(CP, RETIRE)
3195b9c547cSRui Paulo {
3205b9c547cSRui Paulo 	SM_ENTRY(CP, RETIRE);
3215b9c547cSRui Paulo 	/* RETIRE state machine not keep with Figure 12-2 in
3225b9c547cSRui Paulo 	 * IEEE Std 802.1X-2010 */
3235b9c547cSRui Paulo 	os_free(sm->oki);
3245b9c547cSRui Paulo 	sm->oki = NULL;
3255b9c547cSRui Paulo 	sm->orx = FALSE;
3265b9c547cSRui Paulo 	sm->otx = FALSE;
3275b9c547cSRui Paulo 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
3285b9c547cSRui Paulo 				       sm->otx, sm->orx);
3295b9c547cSRui Paulo }
3305b9c547cSRui Paulo 
3315b9c547cSRui Paulo 
3325b9c547cSRui Paulo /**
3335b9c547cSRui Paulo  * CP state machine handler entry
3345b9c547cSRui Paulo  */
3355b9c547cSRui Paulo SM_STEP(CP)
3365b9c547cSRui Paulo {
3375b9c547cSRui Paulo 	if (!sm->port_enabled)
3385b9c547cSRui Paulo 		SM_ENTER(CP, INIT);
3395b9c547cSRui Paulo 
3405b9c547cSRui Paulo 	switch (sm->CP_state) {
3415b9c547cSRui Paulo 	case CP_BEGIN:
3425b9c547cSRui Paulo 		SM_ENTER(CP, INIT);
3435b9c547cSRui Paulo 		break;
3445b9c547cSRui Paulo 
3455b9c547cSRui Paulo 	case CP_INIT:
3465b9c547cSRui Paulo 		SM_ENTER(CP, CHANGE);
3475b9c547cSRui Paulo 		break;
3485b9c547cSRui Paulo 
3495b9c547cSRui Paulo 	case CP_CHANGE:
3505b9c547cSRui Paulo 		if (sm->connect == UNAUTHENTICATED)
3515b9c547cSRui Paulo 			SM_ENTER(CP, ALLOWED);
3525b9c547cSRui Paulo 		else if (sm->connect == AUTHENTICATED)
3535b9c547cSRui Paulo 			SM_ENTER(CP, AUTHENTICATED);
3545b9c547cSRui Paulo 		else if (sm->connect == SECURE)
3555b9c547cSRui Paulo 			SM_ENTER(CP, SECURED);
3565b9c547cSRui Paulo 		break;
3575b9c547cSRui Paulo 
3585b9c547cSRui Paulo 	case CP_ALLOWED:
3595b9c547cSRui Paulo 		if (sm->connect != UNAUTHENTICATED)
3605b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3615b9c547cSRui Paulo 		break;
3625b9c547cSRui Paulo 
3635b9c547cSRui Paulo 	case CP_AUTHENTICATED:
3645b9c547cSRui Paulo 		if (sm->connect != AUTHENTICATED)
3655b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3665b9c547cSRui Paulo 		break;
3675b9c547cSRui Paulo 
3685b9c547cSRui Paulo 	case CP_SECURED:
3695b9c547cSRui Paulo 		if (changed_connect(sm))
3705b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3715b9c547cSRui Paulo 		else if (sm->new_sak)
3725b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
3735b9c547cSRui Paulo 		break;
3745b9c547cSRui Paulo 
3755b9c547cSRui Paulo 	case CP_RECEIVE:
3765b9c547cSRui Paulo 		if (sm->using_receive_sas)
3775b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVING);
3785b9c547cSRui Paulo 		break;
3795b9c547cSRui Paulo 
3805b9c547cSRui Paulo 	case CP_RECEIVING:
3815b9c547cSRui Paulo 		if (sm->new_sak || changed_connect(sm))
3825b9c547cSRui Paulo 			SM_ENTER(CP, ABANDON);
3835b9c547cSRui Paulo 		if (!sm->elected_self)
3845b9c547cSRui Paulo 			SM_ENTER(CP, READY);
3855b9c547cSRui Paulo 		if (sm->elected_self &&
3865b9c547cSRui Paulo 		    (sm->all_receiving || !sm->transmit_when))
3875b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMIT);
3885b9c547cSRui Paulo 		break;
3895b9c547cSRui Paulo 
3905b9c547cSRui Paulo 	case CP_TRANSMIT:
3915b9c547cSRui Paulo 		if (sm->using_transmit_sa)
3925b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMITTING);
3935b9c547cSRui Paulo 		break;
3945b9c547cSRui Paulo 
3955b9c547cSRui Paulo 	case CP_TRANSMITTING:
3965b9c547cSRui Paulo 		if (!sm->retire_when || changed_connect(sm))
3975b9c547cSRui Paulo 			SM_ENTER(CP, RETIRE);
3985b9c547cSRui Paulo 		break;
3995b9c547cSRui Paulo 
4005b9c547cSRui Paulo 	case CP_RETIRE:
4015b9c547cSRui Paulo 		if (changed_connect(sm))
4025b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
4035b9c547cSRui Paulo 		else if (sm->new_sak)
4045b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
4055b9c547cSRui Paulo 		break;
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo 	case CP_READY:
4085b9c547cSRui Paulo 		if (sm->new_sak || changed_connect(sm))
4095b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
4105b9c547cSRui Paulo 		if (sm->server_transmitting)
4115b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMIT);
4125b9c547cSRui Paulo 		break;
4135b9c547cSRui Paulo 	case CP_ABANDON:
4145b9c547cSRui Paulo 		if (changed_connect(sm))
4155b9c547cSRui Paulo 			SM_ENTER(CP, RETIRE);
4165b9c547cSRui Paulo 		else if (sm->new_sak)
4175b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
4185b9c547cSRui Paulo 		break;
4195b9c547cSRui Paulo 	default:
4205b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
4215b9c547cSRui Paulo 		break;
4225b9c547cSRui Paulo 	}
4235b9c547cSRui Paulo }
4245b9c547cSRui Paulo 
4255b9c547cSRui Paulo 
4265b9c547cSRui Paulo /**
4275b9c547cSRui Paulo  * ieee802_1x_cp_sm_init -
4285b9c547cSRui Paulo  */
429780fb4a2SCy Schubert struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
4305b9c547cSRui Paulo {
4315b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm;
4325b9c547cSRui Paulo 
4335b9c547cSRui Paulo 	sm = os_zalloc(sizeof(*sm));
4345b9c547cSRui Paulo 	if (sm == NULL) {
4355b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
4365b9c547cSRui Paulo 		return NULL;
4375b9c547cSRui Paulo 	}
4385b9c547cSRui Paulo 
4395b9c547cSRui Paulo 	sm->kay = kay;
4405b9c547cSRui Paulo 
4415b9c547cSRui Paulo 	sm->port_valid = FALSE;
4425b9c547cSRui Paulo 
4435b9c547cSRui Paulo 	sm->chgd_server = FALSE;
4445b9c547cSRui Paulo 
445780fb4a2SCy Schubert 	sm->protect_frames = kay->macsec_protect;
446780fb4a2SCy Schubert 	sm->validate_frames = kay->macsec_validate;
447780fb4a2SCy Schubert 	sm->replay_protect = kay->macsec_replay_protect;
448780fb4a2SCy Schubert 	sm->replay_window = kay->macsec_replay_window;
4495b9c547cSRui Paulo 
4505b9c547cSRui Paulo 	sm->controlled_port_enabled = FALSE;
4515b9c547cSRui Paulo 
4525b9c547cSRui Paulo 	sm->lki = NULL;
4535b9c547cSRui Paulo 	sm->lrx = FALSE;
4545b9c547cSRui Paulo 	sm->ltx = FALSE;
4555b9c547cSRui Paulo 	sm->oki = NULL;
4565b9c547cSRui Paulo 	sm->orx = FALSE;
4575b9c547cSRui Paulo 	sm->otx = FALSE;
4585b9c547cSRui Paulo 
459780fb4a2SCy Schubert 	sm->current_cipher_suite = default_cs_id;
460780fb4a2SCy Schubert 	sm->cipher_suite = default_cs_id;
4615b9c547cSRui Paulo 	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
4625b9c547cSRui Paulo 	sm->confidentiality_offset = sm->cipher_offset;
4635b9c547cSRui Paulo 	sm->transmit_delay = MKA_LIFE_TIME;
4645b9c547cSRui Paulo 	sm->retire_delay = MKA_SAK_RETIRE_TIME;
4655b9c547cSRui Paulo 	sm->CP_state = CP_BEGIN;
4665b9c547cSRui Paulo 	sm->changed = FALSE;
4675b9c547cSRui Paulo 	sm->authorization_data = NULL;
4685b9c547cSRui Paulo 
4695b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "CP: state machine created");
4705b9c547cSRui Paulo 
4715b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
472*85732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
4735b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
4745b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
4755b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
4765b9c547cSRui Paulo 	secy_cp_control_confidentiality_offset(sm->kay,
4775b9c547cSRui Paulo 					       sm->confidentiality_offset);
4785b9c547cSRui Paulo 
4795b9c547cSRui Paulo 	SM_ENTER(CP, INIT);
4805b9c547cSRui Paulo 	SM_STEP_RUN(CP);
4815b9c547cSRui Paulo 
4825b9c547cSRui Paulo 	return sm;
4835b9c547cSRui Paulo }
4845b9c547cSRui Paulo 
4855b9c547cSRui Paulo 
4865b9c547cSRui Paulo static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
4875b9c547cSRui Paulo {
4885b9c547cSRui Paulo 	enum cp_states prev_state;
4895b9c547cSRui Paulo 	int i;
4905b9c547cSRui Paulo 
4915b9c547cSRui Paulo 	for (i = 0; i < 100; i++) {
4925b9c547cSRui Paulo 		prev_state = sm->CP_state;
4935b9c547cSRui Paulo 		SM_STEP_RUN(CP);
4945b9c547cSRui Paulo 		if (prev_state == sm->CP_state)
4955b9c547cSRui Paulo 			break;
4965b9c547cSRui Paulo 	}
4975b9c547cSRui Paulo }
4985b9c547cSRui Paulo 
4995b9c547cSRui Paulo 
5005b9c547cSRui Paulo static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
5015b9c547cSRui Paulo {
5025b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
5035b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
5045b9c547cSRui Paulo }
5055b9c547cSRui Paulo 
5065b9c547cSRui Paulo 
5075b9c547cSRui Paulo /**
5085b9c547cSRui Paulo  * ieee802_1x_cp_sm_deinit -
5095b9c547cSRui Paulo  */
5105b9c547cSRui Paulo void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
5115b9c547cSRui Paulo {
5125b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "CP: state machine removed");
5135b9c547cSRui Paulo 	if (!sm)
5145b9c547cSRui Paulo 		return;
5155b9c547cSRui Paulo 
5165b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
5175b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
5185b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
5195b9c547cSRui Paulo 	os_free(sm->lki);
5205b9c547cSRui Paulo 	os_free(sm->oki);
5215b9c547cSRui Paulo 	os_free(sm->authorization_data);
5225b9c547cSRui Paulo 	os_free(sm);
5235b9c547cSRui Paulo }
5245b9c547cSRui Paulo 
5255b9c547cSRui Paulo 
5265b9c547cSRui Paulo /**
5275b9c547cSRui Paulo  * ieee802_1x_cp_connect_pending
5285b9c547cSRui Paulo  */
5295b9c547cSRui Paulo void ieee802_1x_cp_connect_pending(void *cp_ctx)
5305b9c547cSRui Paulo {
5315b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5325b9c547cSRui Paulo 
5335b9c547cSRui Paulo 	sm->connect = PENDING;
5345b9c547cSRui Paulo }
5355b9c547cSRui Paulo 
5365b9c547cSRui Paulo 
5375b9c547cSRui Paulo /**
5385b9c547cSRui Paulo  * ieee802_1x_cp_connect_unauthenticated
5395b9c547cSRui Paulo  */
5405b9c547cSRui Paulo void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
5415b9c547cSRui Paulo {
5425b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
5435b9c547cSRui Paulo 
5445b9c547cSRui Paulo 	sm->connect = UNAUTHENTICATED;
5455b9c547cSRui Paulo }
5465b9c547cSRui Paulo 
5475b9c547cSRui Paulo 
5485b9c547cSRui Paulo /**
5495b9c547cSRui Paulo  * ieee802_1x_cp_connect_authenticated
5505b9c547cSRui Paulo  */
5515b9c547cSRui Paulo void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
5525b9c547cSRui Paulo {
5535b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5545b9c547cSRui Paulo 
5555b9c547cSRui Paulo 	sm->connect = AUTHENTICATED;
5565b9c547cSRui Paulo }
5575b9c547cSRui Paulo 
5585b9c547cSRui Paulo 
5595b9c547cSRui Paulo /**
5605b9c547cSRui Paulo  * ieee802_1x_cp_connect_secure
5615b9c547cSRui Paulo  */
5625b9c547cSRui Paulo void ieee802_1x_cp_connect_secure(void *cp_ctx)
5635b9c547cSRui Paulo {
5645b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5655b9c547cSRui Paulo 
5665b9c547cSRui Paulo 	sm->connect = SECURE;
5675b9c547cSRui Paulo }
5685b9c547cSRui Paulo 
5695b9c547cSRui Paulo 
5705b9c547cSRui Paulo /**
5715b9c547cSRui Paulo  * ieee802_1x_cp_set_chgdserver -
5725b9c547cSRui Paulo  */
5735b9c547cSRui Paulo void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
5745b9c547cSRui Paulo {
5755b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5765b9c547cSRui Paulo 
5775b9c547cSRui Paulo 	sm->chgd_server = TRUE;
5785b9c547cSRui Paulo }
5795b9c547cSRui Paulo 
5805b9c547cSRui Paulo 
5815b9c547cSRui Paulo /**
5825b9c547cSRui Paulo  * ieee802_1x_cp_set_electedself -
5835b9c547cSRui Paulo  */
5845b9c547cSRui Paulo void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
5855b9c547cSRui Paulo {
5865b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5875b9c547cSRui Paulo 	sm->elected_self = status;
5885b9c547cSRui Paulo }
5895b9c547cSRui Paulo 
5905b9c547cSRui Paulo 
5915b9c547cSRui Paulo /**
5925b9c547cSRui Paulo  * ieee802_1x_cp_set_authorizationdata -
5935b9c547cSRui Paulo  */
5945b9c547cSRui Paulo void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len)
5955b9c547cSRui Paulo {
5965b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5975b9c547cSRui Paulo 	os_free(sm->authorization_data);
5985b9c547cSRui Paulo 	sm->authorization_data = os_zalloc(len);
5995b9c547cSRui Paulo 	if (sm->authorization_data)
6005b9c547cSRui Paulo 		os_memcpy(sm->authorization_data, pdata, len);
6015b9c547cSRui Paulo }
6025b9c547cSRui Paulo 
6035b9c547cSRui Paulo 
6045b9c547cSRui Paulo /**
6055b9c547cSRui Paulo  * ieee802_1x_cp_set_ciphersuite -
6065b9c547cSRui Paulo  */
607780fb4a2SCy Schubert void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
6085b9c547cSRui Paulo {
6095b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
610780fb4a2SCy Schubert 	sm->cipher_suite = cs;
6115b9c547cSRui Paulo }
6125b9c547cSRui Paulo 
6135b9c547cSRui Paulo 
6145b9c547cSRui Paulo /**
6155b9c547cSRui Paulo  * ieee802_1x_cp_set_offset -
6165b9c547cSRui Paulo  */
6175b9c547cSRui Paulo void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
6185b9c547cSRui Paulo {
6195b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6205b9c547cSRui Paulo 	sm->cipher_offset = offset;
6215b9c547cSRui Paulo }
6225b9c547cSRui Paulo 
6235b9c547cSRui Paulo 
6245b9c547cSRui Paulo /**
6255b9c547cSRui Paulo  * ieee802_1x_cp_signal_newsak -
6265b9c547cSRui Paulo  */
6275b9c547cSRui Paulo void ieee802_1x_cp_signal_newsak(void *cp_ctx)
6285b9c547cSRui Paulo {
6295b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6305b9c547cSRui Paulo 	sm->new_sak = TRUE;
6315b9c547cSRui Paulo }
6325b9c547cSRui Paulo 
6335b9c547cSRui Paulo 
6345b9c547cSRui Paulo /**
6355b9c547cSRui Paulo  * ieee802_1x_cp_set_distributedki -
6365b9c547cSRui Paulo  */
6375b9c547cSRui Paulo void ieee802_1x_cp_set_distributedki(void *cp_ctx,
6385b9c547cSRui Paulo 				     const struct ieee802_1x_mka_ki *dki)
6395b9c547cSRui Paulo {
6405b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6415b9c547cSRui Paulo 	os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
6425b9c547cSRui Paulo }
6435b9c547cSRui Paulo 
6445b9c547cSRui Paulo 
6455b9c547cSRui Paulo /**
6465b9c547cSRui Paulo  * ieee802_1x_cp_set_distributedan -
6475b9c547cSRui Paulo  */
6485b9c547cSRui Paulo void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
6495b9c547cSRui Paulo {
6505b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6515b9c547cSRui Paulo 	sm->distributed_an = an;
6525b9c547cSRui Paulo }
6535b9c547cSRui Paulo 
6545b9c547cSRui Paulo 
6555b9c547cSRui Paulo /**
6565b9c547cSRui Paulo  * ieee802_1x_cp_set_usingreceivesas -
6575b9c547cSRui Paulo  */
6585b9c547cSRui Paulo void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status)
6595b9c547cSRui Paulo {
6605b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6615b9c547cSRui Paulo 	sm->using_receive_sas = status;
6625b9c547cSRui Paulo }
6635b9c547cSRui Paulo 
6645b9c547cSRui Paulo 
6655b9c547cSRui Paulo /**
6665b9c547cSRui Paulo  * ieee802_1x_cp_set_allreceiving -
6675b9c547cSRui Paulo  */
6685b9c547cSRui Paulo void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status)
6695b9c547cSRui Paulo {
6705b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6715b9c547cSRui Paulo 	sm->all_receiving = status;
6725b9c547cSRui Paulo }
6735b9c547cSRui Paulo 
6745b9c547cSRui Paulo 
6755b9c547cSRui Paulo /**
6765b9c547cSRui Paulo  * ieee802_1x_cp_set_servertransmitting -
6775b9c547cSRui Paulo  */
6785b9c547cSRui Paulo void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status)
6795b9c547cSRui Paulo {
6805b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6815b9c547cSRui Paulo 	sm->server_transmitting = status;
6825b9c547cSRui Paulo }
6835b9c547cSRui Paulo 
6845b9c547cSRui Paulo 
6855b9c547cSRui Paulo /**
6865b9c547cSRui Paulo  * ieee802_1x_cp_set_usingtransmitsas -
6875b9c547cSRui Paulo  */
6885b9c547cSRui Paulo void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)
6895b9c547cSRui Paulo {
6905b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6915b9c547cSRui Paulo 	sm->using_transmit_sa = status;
6925b9c547cSRui Paulo }
6935b9c547cSRui Paulo 
6945b9c547cSRui Paulo 
6955b9c547cSRui Paulo /**
6965b9c547cSRui Paulo  * ieee802_1x_cp_sm_step - Advance EAPOL state machines
6975b9c547cSRui Paulo  * @sm: EAPOL state machine
6985b9c547cSRui Paulo  *
6995b9c547cSRui Paulo  * This function is called to advance CP state machines after any change
7005b9c547cSRui Paulo  * that could affect their state.
7015b9c547cSRui Paulo  */
7025b9c547cSRui Paulo void ieee802_1x_cp_sm_step(void *cp_ctx)
7035b9c547cSRui Paulo {
7045b9c547cSRui Paulo 	/*
7055b9c547cSRui Paulo 	 * Run ieee802_1x_cp_step_run from a registered timeout
7065b9c547cSRui Paulo 	 * to make sure that other possible timeouts/events are processed
7075b9c547cSRui Paulo 	 * and to avoid long function call chains.
7085b9c547cSRui Paulo 	 */
7095b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
7105b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
7115b9c547cSRui Paulo 	eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
7125b9c547cSRui Paulo }
7135b9c547cSRui Paulo 
7145b9c547cSRui Paulo 
7155b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
7165b9c547cSRui Paulo 					      void *timeout_ctx)
7175b9c547cSRui Paulo {
7185b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
7195b9c547cSRui Paulo 	sm->retire_when = 0;
7205b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
7215b9c547cSRui Paulo }
7225b9c547cSRui Paulo 
7235b9c547cSRui Paulo 
7245b9c547cSRui Paulo static void
7255b9c547cSRui Paulo ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
7265b9c547cSRui Paulo {
7275b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
7285b9c547cSRui Paulo 	sm->transmit_when = 0;
7295b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
7305b9c547cSRui Paulo }
731