xref: /freebsd/contrib/wpa/src/pae/ieee802_1x_cp.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
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;
34*c1d255d3SCy Schubert 	bool changed;
355b9c547cSRui Paulo 
365b9c547cSRui Paulo 	/* CP -> Client */
37*c1d255d3SCy Schubert 	bool port_valid;
385b9c547cSRui Paulo 
395b9c547cSRui Paulo 	/* Logon -> CP */
405b9c547cSRui Paulo 	enum connect_type connect;
415b9c547cSRui Paulo 
425b9c547cSRui Paulo 	/* KaY -> CP */
43*c1d255d3SCy Schubert 	bool chgd_server; /* clear by CP */
44*c1d255d3SCy Schubert 	bool elected_self;
455b9c547cSRui Paulo 	enum confidentiality_offset cipher_offset;
46780fb4a2SCy Schubert 	u64 cipher_suite;
47*c1d255d3SCy Schubert 	bool new_sak; /* clear by CP */
485b9c547cSRui Paulo 	struct ieee802_1x_mka_ki distributed_ki;
495b9c547cSRui Paulo 	u8 distributed_an;
50*c1d255d3SCy Schubert 	bool using_receive_sas;
51*c1d255d3SCy Schubert 	bool all_receiving;
52*c1d255d3SCy Schubert 	bool server_transmitting;
53*c1d255d3SCy Schubert 	bool using_transmit_sa;
545b9c547cSRui Paulo 
555b9c547cSRui Paulo 	/* CP -> KaY */
565b9c547cSRui Paulo 	struct ieee802_1x_mka_ki *lki;
575b9c547cSRui Paulo 	u8 lan;
58*c1d255d3SCy Schubert 	bool ltx;
59*c1d255d3SCy Schubert 	bool lrx;
605b9c547cSRui Paulo 	struct ieee802_1x_mka_ki *oki;
615b9c547cSRui Paulo 	u8 oan;
62*c1d255d3SCy Schubert 	bool otx;
63*c1d255d3SCy Schubert 	bool orx;
645b9c547cSRui Paulo 
655b9c547cSRui Paulo 	/* CP -> SecY */
66*c1d255d3SCy Schubert 	bool protect_frames;
675b9c547cSRui Paulo 	enum validate_frames validate_frames;
685b9c547cSRui Paulo 
69*c1d255d3SCy Schubert 	bool replay_protect;
705b9c547cSRui Paulo 	u32 replay_window;
715b9c547cSRui Paulo 
72780fb4a2SCy Schubert 	u64 current_cipher_suite;
735b9c547cSRui Paulo 	enum confidentiality_offset confidentiality_offset;
74*c1d255d3SCy Schubert 	bool controlled_port_enabled;
755b9c547cSRui Paulo 
765b9c547cSRui Paulo 	/* SecY -> CP */
77*c1d255d3SCy Schubert 	bool port_enabled; /* SecY->CP */
785b9c547cSRui Paulo 
795b9c547cSRui Paulo 	/* private */
805b9c547cSRui Paulo 	u32 transmit_when;
815b9c547cSRui Paulo 	u32 transmit_delay;
825b9c547cSRui Paulo 	u32 retire_when;
835b9c547cSRui Paulo 	u32 retire_delay;
845b9c547cSRui Paulo 
855b9c547cSRui Paulo 	/* not defined IEEE Std 802.1X-2010 */
865b9c547cSRui Paulo 	struct ieee802_1x_kay *kay;
875b9c547cSRui Paulo };
885b9c547cSRui Paulo 
895b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
905b9c547cSRui Paulo 					      void *timeout_ctx);
915b9c547cSRui Paulo static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
925b9c547cSRui Paulo 						void *timeout_ctx);
935b9c547cSRui Paulo 
945b9c547cSRui Paulo 
955b9c547cSRui Paulo static int changed_cipher(struct ieee802_1x_cp_sm *sm)
965b9c547cSRui Paulo {
975b9c547cSRui Paulo 	return sm->confidentiality_offset != sm->cipher_offset ||
98780fb4a2SCy Schubert 		sm->current_cipher_suite != sm->cipher_suite;
995b9c547cSRui Paulo }
1005b9c547cSRui Paulo 
1015b9c547cSRui Paulo 
1025b9c547cSRui Paulo static int changed_connect(struct ieee802_1x_cp_sm *sm)
1035b9c547cSRui Paulo {
1045b9c547cSRui Paulo 	return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
1055b9c547cSRui Paulo }
1065b9c547cSRui Paulo 
1075b9c547cSRui Paulo 
1085b9c547cSRui Paulo SM_STATE(CP, INIT)
1095b9c547cSRui Paulo {
1105b9c547cSRui Paulo 	SM_ENTRY(CP, INIT);
1115b9c547cSRui Paulo 
112*c1d255d3SCy Schubert 	sm->controlled_port_enabled = false;
1135b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1145b9c547cSRui Paulo 
115*c1d255d3SCy Schubert 	sm->port_valid = false;
1165b9c547cSRui Paulo 
1175b9c547cSRui Paulo 	os_free(sm->lki);
1185b9c547cSRui Paulo 	sm->lki = NULL;
119*c1d255d3SCy Schubert 	sm->ltx = false;
120*c1d255d3SCy Schubert 	sm->lrx = false;
1215b9c547cSRui Paulo 
1225b9c547cSRui Paulo 	os_free(sm->oki);
1235b9c547cSRui Paulo 	sm->oki = NULL;
124*c1d255d3SCy Schubert 	sm->otx = false;
125*c1d255d3SCy Schubert 	sm->orx = false;
1265b9c547cSRui Paulo 
127*c1d255d3SCy Schubert 	sm->port_enabled = true;
128*c1d255d3SCy Schubert 	sm->chgd_server = false;
1295b9c547cSRui Paulo }
1305b9c547cSRui Paulo 
1315b9c547cSRui Paulo 
1325b9c547cSRui Paulo SM_STATE(CP, CHANGE)
1335b9c547cSRui Paulo {
1345b9c547cSRui Paulo 	SM_ENTRY(CP, CHANGE);
1355b9c547cSRui Paulo 
136*c1d255d3SCy Schubert 	sm->port_valid = false;
137*c1d255d3SCy Schubert 	sm->controlled_port_enabled = false;
1385b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1395b9c547cSRui Paulo 
1405b9c547cSRui Paulo 	if (sm->lki)
1415b9c547cSRui Paulo 		ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
1425b9c547cSRui Paulo 	if (sm->oki)
1435b9c547cSRui Paulo 		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
144*c1d255d3SCy Schubert 	/* The standard doesn't say it but we should clear out the latest
145*c1d255d3SCy Schubert 	 * and old key values. Why would we keep advertising them if
146*c1d255d3SCy Schubert 	 * they've been deleted and the key server has been changed?
147*c1d255d3SCy Schubert 	 */
148*c1d255d3SCy Schubert 	os_free(sm->oki);
149*c1d255d3SCy Schubert 	sm->oki = NULL;
150*c1d255d3SCy Schubert 	sm->otx = false;
151*c1d255d3SCy Schubert 	sm->orx = false;
152*c1d255d3SCy Schubert 	sm->oan = 0;
153*c1d255d3SCy Schubert 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
154*c1d255d3SCy Schubert 				       sm->otx, sm->orx);
155*c1d255d3SCy Schubert 	os_free(sm->lki);
156*c1d255d3SCy Schubert 	sm->lki = NULL;
157*c1d255d3SCy Schubert 	sm->lrx = false;
158*c1d255d3SCy Schubert 	sm->ltx = false;
159*c1d255d3SCy Schubert 	sm->lan = 0;
160*c1d255d3SCy Schubert 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
161*c1d255d3SCy Schubert 					  sm->ltx, sm->lrx);
1625b9c547cSRui Paulo }
1635b9c547cSRui Paulo 
1645b9c547cSRui Paulo 
1655b9c547cSRui Paulo SM_STATE(CP, ALLOWED)
1665b9c547cSRui Paulo {
1675b9c547cSRui Paulo 	SM_ENTRY(CP, ALLOWED);
1685b9c547cSRui Paulo 
169*c1d255d3SCy Schubert 	sm->protect_frames = false;
170*c1d255d3SCy Schubert 	sm->replay_protect = false;
1715b9c547cSRui Paulo 	sm->validate_frames = Checked;
1725b9c547cSRui Paulo 
173*c1d255d3SCy Schubert 	sm->port_valid = false;
174*c1d255d3SCy Schubert 	sm->controlled_port_enabled = true;
1755b9c547cSRui Paulo 
1765b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1775b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
17885732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
1795b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
1805b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
1815b9c547cSRui Paulo }
1825b9c547cSRui Paulo 
1835b9c547cSRui Paulo 
1845b9c547cSRui Paulo SM_STATE(CP, AUTHENTICATED)
1855b9c547cSRui Paulo {
1865b9c547cSRui Paulo 	SM_ENTRY(CP, AUTHENTICATED);
1875b9c547cSRui Paulo 
188*c1d255d3SCy Schubert 	sm->protect_frames = false;
189*c1d255d3SCy Schubert 	sm->replay_protect = false;
1905b9c547cSRui Paulo 	sm->validate_frames = Checked;
1915b9c547cSRui Paulo 
192*c1d255d3SCy Schubert 	sm->port_valid = false;
193*c1d255d3SCy Schubert 	sm->controlled_port_enabled = true;
1945b9c547cSRui Paulo 
1955b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
1965b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
19785732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
1985b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
1995b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
2005b9c547cSRui Paulo }
2015b9c547cSRui Paulo 
2025b9c547cSRui Paulo 
2035b9c547cSRui Paulo SM_STATE(CP, SECURED)
2045b9c547cSRui Paulo {
2055b9c547cSRui Paulo 	SM_ENTRY(CP, SECURED);
2065b9c547cSRui Paulo 
207*c1d255d3SCy Schubert 	sm->chgd_server = false;
2085b9c547cSRui Paulo 
209780fb4a2SCy Schubert 	sm->protect_frames = sm->kay->macsec_protect;
210780fb4a2SCy Schubert 	sm->replay_protect = sm->kay->macsec_replay_protect;
211780fb4a2SCy Schubert 	sm->validate_frames = sm->kay->macsec_validate;
2125b9c547cSRui Paulo 
213780fb4a2SCy Schubert 	/* NOTE: now no other than default cipher suite (AES-GCM-128) */
214780fb4a2SCy Schubert 	sm->current_cipher_suite = sm->cipher_suite;
215780fb4a2SCy Schubert 	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
2165b9c547cSRui Paulo 
2175b9c547cSRui Paulo 	sm->confidentiality_offset = sm->cipher_offset;
2185b9c547cSRui Paulo 
219*c1d255d3SCy Schubert 	sm->port_valid = true;
2205b9c547cSRui Paulo 
2215b9c547cSRui Paulo 	secy_cp_control_confidentiality_offset(sm->kay,
2225b9c547cSRui Paulo 					       sm->confidentiality_offset);
2235b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
22485732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
2255b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
2265b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
2275b9c547cSRui Paulo }
2285b9c547cSRui Paulo 
2295b9c547cSRui Paulo 
2305b9c547cSRui Paulo SM_STATE(CP, RECEIVE)
2315b9c547cSRui Paulo {
2325b9c547cSRui Paulo 	SM_ENTRY(CP, RECEIVE);
2335b9c547cSRui Paulo 
2345b9c547cSRui Paulo 	sm->lki = os_malloc(sizeof(*sm->lki));
2355b9c547cSRui Paulo 	if (!sm->lki) {
2365b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
2375b9c547cSRui Paulo 		return;
2385b9c547cSRui Paulo 	}
2395b9c547cSRui Paulo 	os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
2405b9c547cSRui Paulo 	sm->lan = sm->distributed_an;
241*c1d255d3SCy Schubert 	sm->ltx = false;
242*c1d255d3SCy Schubert 	sm->lrx = false;
2435b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2445b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2455b9c547cSRui Paulo 	ieee802_1x_kay_create_sas(sm->kay, sm->lki);
2465b9c547cSRui Paulo 	ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
247*c1d255d3SCy Schubert 	sm->new_sak = false;
248*c1d255d3SCy Schubert 	sm->all_receiving = false;
2495b9c547cSRui Paulo }
2505b9c547cSRui Paulo 
2515b9c547cSRui Paulo 
2525b9c547cSRui Paulo SM_STATE(CP, RECEIVING)
2535b9c547cSRui Paulo {
2545b9c547cSRui Paulo 	SM_ENTRY(CP, RECEIVING);
2555b9c547cSRui Paulo 
256*c1d255d3SCy Schubert 	sm->lrx = true;
2575b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2585b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2595b9c547cSRui Paulo 	sm->transmit_when = sm->transmit_delay;
2605b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
2615b9c547cSRui Paulo 	eloop_register_timeout(sm->transmit_when / 1000, 0,
2625b9c547cSRui Paulo 			       ieee802_1x_cp_transmit_when_timeout, sm, NULL);
2635b9c547cSRui Paulo 	/* the electedSelf have been set before CP entering to RECEIVING
2645b9c547cSRui Paulo 	 * but the CP will transmit from RECEIVING to READY under
2655b9c547cSRui Paulo 	 * the !electedSelf when KaY is not key server */
2665b9c547cSRui Paulo 	ieee802_1x_cp_sm_step(sm);
267*c1d255d3SCy Schubert 	sm->using_receive_sas = false;
268*c1d255d3SCy Schubert 	sm->server_transmitting = false;
2695b9c547cSRui Paulo }
2705b9c547cSRui Paulo 
2715b9c547cSRui Paulo 
2725b9c547cSRui Paulo SM_STATE(CP, READY)
2735b9c547cSRui Paulo {
2745b9c547cSRui Paulo 	SM_ENTRY(CP, READY);
2755b9c547cSRui Paulo 
2765b9c547cSRui Paulo 	ieee802_1x_kay_enable_new_info(sm->kay);
2775b9c547cSRui Paulo }
2785b9c547cSRui Paulo 
2795b9c547cSRui Paulo 
2805b9c547cSRui Paulo SM_STATE(CP, TRANSMIT)
2815b9c547cSRui Paulo {
2825b9c547cSRui Paulo 	SM_ENTRY(CP, TRANSMIT);
2835b9c547cSRui Paulo 
284*c1d255d3SCy Schubert 	sm->controlled_port_enabled = true;
2855b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
286*c1d255d3SCy Schubert 	sm->ltx = true;
2875b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
2885b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
2895b9c547cSRui Paulo 	ieee802_1x_kay_enable_tx_sas(sm->kay,  sm->lki);
290*c1d255d3SCy Schubert 	sm->all_receiving = false;
291*c1d255d3SCy Schubert 	sm->server_transmitting = false;
2925b9c547cSRui Paulo }
2935b9c547cSRui Paulo 
2945b9c547cSRui Paulo 
2955b9c547cSRui Paulo SM_STATE(CP, TRANSMITTING)
2965b9c547cSRui Paulo {
2975b9c547cSRui Paulo 	SM_ENTRY(CP, TRANSMITTING);
2985b9c547cSRui Paulo 	sm->retire_when = sm->orx ? sm->retire_delay : 0;
299*c1d255d3SCy Schubert 	sm->otx = false;
3005b9c547cSRui Paulo 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
3015b9c547cSRui Paulo 				       sm->otx, sm->orx);
3025b9c547cSRui Paulo 	ieee802_1x_kay_enable_new_info(sm->kay);
3035b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
3045b9c547cSRui Paulo 	eloop_register_timeout(sm->retire_when / 1000, 0,
3055b9c547cSRui Paulo 			       ieee802_1x_cp_retire_when_timeout, sm, NULL);
306*c1d255d3SCy Schubert 	sm->using_transmit_sa = false;
3075b9c547cSRui Paulo }
3085b9c547cSRui Paulo 
3095b9c547cSRui Paulo 
3105b9c547cSRui Paulo SM_STATE(CP, ABANDON)
3115b9c547cSRui Paulo {
3125b9c547cSRui Paulo 	SM_ENTRY(CP, ABANDON);
313*c1d255d3SCy Schubert 	sm->lrx = false;
3145b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
3155b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
3165b9c547cSRui Paulo 	ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
3175b9c547cSRui Paulo 
3185b9c547cSRui Paulo 	os_free(sm->lki);
3195b9c547cSRui Paulo 	sm->lki = NULL;
3205b9c547cSRui Paulo 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
3215b9c547cSRui Paulo 					  sm->ltx, sm->lrx);
3225b9c547cSRui Paulo }
3235b9c547cSRui Paulo 
3245b9c547cSRui Paulo 
3255b9c547cSRui Paulo SM_STATE(CP, RETIRE)
3265b9c547cSRui Paulo {
3275b9c547cSRui Paulo 	SM_ENTRY(CP, RETIRE);
3284bc52338SCy Schubert 	if (sm->oki) {
3294bc52338SCy Schubert 		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
3305b9c547cSRui Paulo 		os_free(sm->oki);
3315b9c547cSRui Paulo 		sm->oki = NULL;
3324bc52338SCy Schubert 	}
333*c1d255d3SCy Schubert 	sm->oki = sm->lki;
334*c1d255d3SCy Schubert 	sm->otx = sm->ltx;
335*c1d255d3SCy Schubert 	sm->orx = sm->lrx;
336*c1d255d3SCy Schubert 	sm->oan = sm->lan;
3375b9c547cSRui Paulo 	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
3385b9c547cSRui Paulo 				       sm->otx, sm->orx);
339*c1d255d3SCy Schubert 	sm->lki = NULL;
340*c1d255d3SCy Schubert 	sm->ltx = false;
341*c1d255d3SCy Schubert 	sm->lrx = false;
342*c1d255d3SCy Schubert 	sm->lan = 0;
343*c1d255d3SCy Schubert 	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
344*c1d255d3SCy Schubert 					  sm->ltx, sm->lrx);
3455b9c547cSRui Paulo }
3465b9c547cSRui Paulo 
3475b9c547cSRui Paulo 
3485b9c547cSRui Paulo /**
3495b9c547cSRui Paulo  * CP state machine handler entry
3505b9c547cSRui Paulo  */
3515b9c547cSRui Paulo SM_STEP(CP)
3525b9c547cSRui Paulo {
3535b9c547cSRui Paulo 	if (!sm->port_enabled)
3545b9c547cSRui Paulo 		SM_ENTER(CP, INIT);
3555b9c547cSRui Paulo 
3565b9c547cSRui Paulo 	switch (sm->CP_state) {
3575b9c547cSRui Paulo 	case CP_BEGIN:
3585b9c547cSRui Paulo 		SM_ENTER(CP, INIT);
3595b9c547cSRui Paulo 		break;
3605b9c547cSRui Paulo 
3615b9c547cSRui Paulo 	case CP_INIT:
3625b9c547cSRui Paulo 		SM_ENTER(CP, CHANGE);
3635b9c547cSRui Paulo 		break;
3645b9c547cSRui Paulo 
3655b9c547cSRui Paulo 	case CP_CHANGE:
3665b9c547cSRui Paulo 		if (sm->connect == UNAUTHENTICATED)
3675b9c547cSRui Paulo 			SM_ENTER(CP, ALLOWED);
3685b9c547cSRui Paulo 		else if (sm->connect == AUTHENTICATED)
3695b9c547cSRui Paulo 			SM_ENTER(CP, AUTHENTICATED);
3705b9c547cSRui Paulo 		else if (sm->connect == SECURE)
3715b9c547cSRui Paulo 			SM_ENTER(CP, SECURED);
3725b9c547cSRui Paulo 		break;
3735b9c547cSRui Paulo 
3745b9c547cSRui Paulo 	case CP_ALLOWED:
3755b9c547cSRui Paulo 		if (sm->connect != UNAUTHENTICATED)
3765b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3775b9c547cSRui Paulo 		break;
3785b9c547cSRui Paulo 
3795b9c547cSRui Paulo 	case CP_AUTHENTICATED:
3805b9c547cSRui Paulo 		if (sm->connect != AUTHENTICATED)
3815b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3825b9c547cSRui Paulo 		break;
3835b9c547cSRui Paulo 
3845b9c547cSRui Paulo 	case CP_SECURED:
3855b9c547cSRui Paulo 		if (changed_connect(sm))
3865b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
3875b9c547cSRui Paulo 		else if (sm->new_sak)
3885b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
3895b9c547cSRui Paulo 		break;
3905b9c547cSRui Paulo 
3915b9c547cSRui Paulo 	case CP_RECEIVE:
3925b9c547cSRui Paulo 		if (sm->using_receive_sas)
3935b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVING);
3945b9c547cSRui Paulo 		break;
3955b9c547cSRui Paulo 
3965b9c547cSRui Paulo 	case CP_RECEIVING:
3975b9c547cSRui Paulo 		if (sm->new_sak || changed_connect(sm))
3985b9c547cSRui Paulo 			SM_ENTER(CP, ABANDON);
3995b9c547cSRui Paulo 		if (!sm->elected_self)
4005b9c547cSRui Paulo 			SM_ENTER(CP, READY);
4015b9c547cSRui Paulo 		if (sm->elected_self &&
4024bc52338SCy Schubert 		    (sm->all_receiving || !sm->controlled_port_enabled ||
4034bc52338SCy Schubert 		     !sm->transmit_when))
4045b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMIT);
4055b9c547cSRui Paulo 		break;
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo 	case CP_TRANSMIT:
4085b9c547cSRui Paulo 		if (sm->using_transmit_sa)
4095b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMITTING);
4105b9c547cSRui Paulo 		break;
4115b9c547cSRui Paulo 
4125b9c547cSRui Paulo 	case CP_TRANSMITTING:
4135b9c547cSRui Paulo 		if (!sm->retire_when || changed_connect(sm))
4145b9c547cSRui Paulo 			SM_ENTER(CP, RETIRE);
4155b9c547cSRui Paulo 		break;
4165b9c547cSRui Paulo 
4175b9c547cSRui Paulo 	case CP_RETIRE:
4185b9c547cSRui Paulo 		if (changed_connect(sm))
4195b9c547cSRui Paulo 			SM_ENTER(CP, CHANGE);
4205b9c547cSRui Paulo 		else if (sm->new_sak)
4215b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
4225b9c547cSRui Paulo 		break;
4235b9c547cSRui Paulo 
4245b9c547cSRui Paulo 	case CP_READY:
4255b9c547cSRui Paulo 		if (sm->new_sak || changed_connect(sm))
4264bc52338SCy Schubert 			SM_ENTER(CP, ABANDON);
4274bc52338SCy Schubert 		if (sm->server_transmitting || !sm->controlled_port_enabled)
4285b9c547cSRui Paulo 			SM_ENTER(CP, TRANSMIT);
4295b9c547cSRui Paulo 		break;
4305b9c547cSRui Paulo 	case CP_ABANDON:
4315b9c547cSRui Paulo 		if (changed_connect(sm))
4325b9c547cSRui Paulo 			SM_ENTER(CP, RETIRE);
4335b9c547cSRui Paulo 		else if (sm->new_sak)
4345b9c547cSRui Paulo 			SM_ENTER(CP, RECEIVE);
4355b9c547cSRui Paulo 		break;
4365b9c547cSRui Paulo 	default:
4375b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
4385b9c547cSRui Paulo 		break;
4395b9c547cSRui Paulo 	}
4405b9c547cSRui Paulo }
4415b9c547cSRui Paulo 
4425b9c547cSRui Paulo 
4435b9c547cSRui Paulo /**
4445b9c547cSRui Paulo  * ieee802_1x_cp_sm_init -
4455b9c547cSRui Paulo  */
446780fb4a2SCy Schubert struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
4475b9c547cSRui Paulo {
4485b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm;
4495b9c547cSRui Paulo 
4505b9c547cSRui Paulo 	sm = os_zalloc(sizeof(*sm));
4515b9c547cSRui Paulo 	if (sm == NULL) {
4525b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
4535b9c547cSRui Paulo 		return NULL;
4545b9c547cSRui Paulo 	}
4555b9c547cSRui Paulo 
4565b9c547cSRui Paulo 	sm->kay = kay;
4575b9c547cSRui Paulo 
458*c1d255d3SCy Schubert 	sm->port_valid = false;
4595b9c547cSRui Paulo 
460*c1d255d3SCy Schubert 	sm->chgd_server = false;
4615b9c547cSRui Paulo 
462780fb4a2SCy Schubert 	sm->protect_frames = kay->macsec_protect;
463780fb4a2SCy Schubert 	sm->validate_frames = kay->macsec_validate;
464780fb4a2SCy Schubert 	sm->replay_protect = kay->macsec_replay_protect;
465780fb4a2SCy Schubert 	sm->replay_window = kay->macsec_replay_window;
4665b9c547cSRui Paulo 
467*c1d255d3SCy Schubert 	sm->controlled_port_enabled = false;
4685b9c547cSRui Paulo 
4695b9c547cSRui Paulo 	sm->lki = NULL;
470*c1d255d3SCy Schubert 	sm->lrx = false;
471*c1d255d3SCy Schubert 	sm->ltx = false;
4725b9c547cSRui Paulo 	sm->oki = NULL;
473*c1d255d3SCy Schubert 	sm->orx = false;
474*c1d255d3SCy Schubert 	sm->otx = false;
4755b9c547cSRui Paulo 
476780fb4a2SCy Schubert 	sm->current_cipher_suite = default_cs_id;
477780fb4a2SCy Schubert 	sm->cipher_suite = default_cs_id;
4785b9c547cSRui Paulo 	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
4795b9c547cSRui Paulo 	sm->confidentiality_offset = sm->cipher_offset;
4805b9c547cSRui Paulo 	sm->transmit_delay = MKA_LIFE_TIME;
4815b9c547cSRui Paulo 	sm->retire_delay = MKA_SAK_RETIRE_TIME;
4825b9c547cSRui Paulo 	sm->CP_state = CP_BEGIN;
483*c1d255d3SCy Schubert 	sm->changed = false;
4845b9c547cSRui Paulo 
4855b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "CP: state machine created");
4865b9c547cSRui Paulo 
4875b9c547cSRui Paulo 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
48885732ac8SCy Schubert 	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
4895b9c547cSRui Paulo 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
4905b9c547cSRui Paulo 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
4915b9c547cSRui Paulo 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
4925b9c547cSRui Paulo 	secy_cp_control_confidentiality_offset(sm->kay,
4935b9c547cSRui Paulo 					       sm->confidentiality_offset);
4945b9c547cSRui Paulo 
4955b9c547cSRui Paulo 	SM_STEP_RUN(CP);
4965b9c547cSRui Paulo 
4975b9c547cSRui Paulo 	return sm;
4985b9c547cSRui Paulo }
4995b9c547cSRui Paulo 
5005b9c547cSRui Paulo 
5015b9c547cSRui Paulo static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
5025b9c547cSRui Paulo {
5035b9c547cSRui Paulo 	enum cp_states prev_state;
5045b9c547cSRui Paulo 	int i;
5055b9c547cSRui Paulo 
5065b9c547cSRui Paulo 	for (i = 0; i < 100; i++) {
5075b9c547cSRui Paulo 		prev_state = sm->CP_state;
5085b9c547cSRui Paulo 		SM_STEP_RUN(CP);
5095b9c547cSRui Paulo 		if (prev_state == sm->CP_state)
5105b9c547cSRui Paulo 			break;
5115b9c547cSRui Paulo 	}
5125b9c547cSRui Paulo }
5135b9c547cSRui Paulo 
5145b9c547cSRui Paulo 
5155b9c547cSRui Paulo static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
5165b9c547cSRui Paulo {
5175b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
5185b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
5195b9c547cSRui Paulo }
5205b9c547cSRui Paulo 
5215b9c547cSRui Paulo 
5225b9c547cSRui Paulo /**
5235b9c547cSRui Paulo  * ieee802_1x_cp_sm_deinit -
5245b9c547cSRui Paulo  */
5255b9c547cSRui Paulo void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
5265b9c547cSRui Paulo {
5275b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "CP: state machine removed");
5285b9c547cSRui Paulo 	if (!sm)
5295b9c547cSRui Paulo 		return;
5305b9c547cSRui Paulo 
5315b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
5325b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
5335b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
5345b9c547cSRui Paulo 	os_free(sm->lki);
5355b9c547cSRui Paulo 	os_free(sm->oki);
5365b9c547cSRui Paulo 	os_free(sm);
5375b9c547cSRui Paulo }
5385b9c547cSRui Paulo 
5395b9c547cSRui Paulo 
5405b9c547cSRui Paulo /**
5415b9c547cSRui Paulo  * ieee802_1x_cp_connect_pending
5425b9c547cSRui Paulo  */
5435b9c547cSRui Paulo void ieee802_1x_cp_connect_pending(void *cp_ctx)
5445b9c547cSRui Paulo {
5455b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5465b9c547cSRui Paulo 
5475b9c547cSRui Paulo 	sm->connect = PENDING;
5485b9c547cSRui Paulo }
5495b9c547cSRui Paulo 
5505b9c547cSRui Paulo 
5515b9c547cSRui Paulo /**
5525b9c547cSRui Paulo  * ieee802_1x_cp_connect_unauthenticated
5535b9c547cSRui Paulo  */
5545b9c547cSRui Paulo void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
5555b9c547cSRui Paulo {
5565b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
5575b9c547cSRui Paulo 
5585b9c547cSRui Paulo 	sm->connect = UNAUTHENTICATED;
5595b9c547cSRui Paulo }
5605b9c547cSRui Paulo 
5615b9c547cSRui Paulo 
5625b9c547cSRui Paulo /**
5635b9c547cSRui Paulo  * ieee802_1x_cp_connect_authenticated
5645b9c547cSRui Paulo  */
5655b9c547cSRui Paulo void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
5665b9c547cSRui Paulo {
5675b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5685b9c547cSRui Paulo 
5695b9c547cSRui Paulo 	sm->connect = AUTHENTICATED;
5705b9c547cSRui Paulo }
5715b9c547cSRui Paulo 
5725b9c547cSRui Paulo 
5735b9c547cSRui Paulo /**
5745b9c547cSRui Paulo  * ieee802_1x_cp_connect_secure
5755b9c547cSRui Paulo  */
5765b9c547cSRui Paulo void ieee802_1x_cp_connect_secure(void *cp_ctx)
5775b9c547cSRui Paulo {
5785b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5795b9c547cSRui Paulo 
5805b9c547cSRui Paulo 	sm->connect = SECURE;
5815b9c547cSRui Paulo }
5825b9c547cSRui Paulo 
5835b9c547cSRui Paulo 
5845b9c547cSRui Paulo /**
5855b9c547cSRui Paulo  * ieee802_1x_cp_set_chgdserver -
5865b9c547cSRui Paulo  */
5875b9c547cSRui Paulo void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
5885b9c547cSRui Paulo {
5895b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
5905b9c547cSRui Paulo 
591*c1d255d3SCy Schubert 	sm->chgd_server = true;
5925b9c547cSRui Paulo }
5935b9c547cSRui Paulo 
5945b9c547cSRui Paulo 
5955b9c547cSRui Paulo /**
5965b9c547cSRui Paulo  * ieee802_1x_cp_set_electedself -
5975b9c547cSRui Paulo  */
598*c1d255d3SCy Schubert void ieee802_1x_cp_set_electedself(void *cp_ctx, bool status)
5995b9c547cSRui Paulo {
6005b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6015b9c547cSRui Paulo 	sm->elected_self = status;
6025b9c547cSRui Paulo }
6035b9c547cSRui Paulo 
6045b9c547cSRui Paulo 
6055b9c547cSRui Paulo /**
6065b9c547cSRui Paulo  * ieee802_1x_cp_set_ciphersuite -
6075b9c547cSRui Paulo  */
608780fb4a2SCy Schubert void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
6095b9c547cSRui Paulo {
6105b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
611780fb4a2SCy Schubert 	sm->cipher_suite = cs;
6125b9c547cSRui Paulo }
6135b9c547cSRui Paulo 
6145b9c547cSRui Paulo 
6155b9c547cSRui Paulo /**
6165b9c547cSRui Paulo  * ieee802_1x_cp_set_offset -
6175b9c547cSRui Paulo  */
6185b9c547cSRui Paulo void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
6195b9c547cSRui Paulo {
6205b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6215b9c547cSRui Paulo 	sm->cipher_offset = offset;
6225b9c547cSRui Paulo }
6235b9c547cSRui Paulo 
6245b9c547cSRui Paulo 
6255b9c547cSRui Paulo /**
6265b9c547cSRui Paulo  * ieee802_1x_cp_signal_newsak -
6275b9c547cSRui Paulo  */
6285b9c547cSRui Paulo void ieee802_1x_cp_signal_newsak(void *cp_ctx)
6295b9c547cSRui Paulo {
6305b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
631*c1d255d3SCy Schubert 	sm->new_sak = true;
6325b9c547cSRui Paulo }
6335b9c547cSRui Paulo 
6345b9c547cSRui Paulo 
6355b9c547cSRui Paulo /**
6365b9c547cSRui Paulo  * ieee802_1x_cp_set_distributedki -
6375b9c547cSRui Paulo  */
6385b9c547cSRui Paulo void ieee802_1x_cp_set_distributedki(void *cp_ctx,
6395b9c547cSRui Paulo 				     const struct ieee802_1x_mka_ki *dki)
6405b9c547cSRui Paulo {
6415b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6425b9c547cSRui Paulo 	os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
6435b9c547cSRui Paulo }
6445b9c547cSRui Paulo 
6455b9c547cSRui Paulo 
6465b9c547cSRui Paulo /**
6475b9c547cSRui Paulo  * ieee802_1x_cp_set_distributedan -
6485b9c547cSRui Paulo  */
6495b9c547cSRui Paulo void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
6505b9c547cSRui Paulo {
6515b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6525b9c547cSRui Paulo 	sm->distributed_an = an;
6535b9c547cSRui Paulo }
6545b9c547cSRui Paulo 
6555b9c547cSRui Paulo 
6565b9c547cSRui Paulo /**
6575b9c547cSRui Paulo  * ieee802_1x_cp_set_usingreceivesas -
6585b9c547cSRui Paulo  */
659*c1d255d3SCy Schubert void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, bool status)
6605b9c547cSRui Paulo {
6615b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6625b9c547cSRui Paulo 	sm->using_receive_sas = status;
6635b9c547cSRui Paulo }
6645b9c547cSRui Paulo 
6655b9c547cSRui Paulo 
6665b9c547cSRui Paulo /**
6675b9c547cSRui Paulo  * ieee802_1x_cp_set_allreceiving -
6685b9c547cSRui Paulo  */
669*c1d255d3SCy Schubert void ieee802_1x_cp_set_allreceiving(void *cp_ctx, bool status)
6705b9c547cSRui Paulo {
6715b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6725b9c547cSRui Paulo 	sm->all_receiving = status;
6735b9c547cSRui Paulo }
6745b9c547cSRui Paulo 
6755b9c547cSRui Paulo 
6765b9c547cSRui Paulo /**
6775b9c547cSRui Paulo  * ieee802_1x_cp_set_servertransmitting -
6785b9c547cSRui Paulo  */
679*c1d255d3SCy Schubert void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, bool status)
6805b9c547cSRui Paulo {
6815b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6825b9c547cSRui Paulo 	sm->server_transmitting = status;
6835b9c547cSRui Paulo }
6845b9c547cSRui Paulo 
6855b9c547cSRui Paulo 
6865b9c547cSRui Paulo /**
6875b9c547cSRui Paulo  * ieee802_1x_cp_set_usingtransmitsas -
6885b9c547cSRui Paulo  */
689*c1d255d3SCy Schubert void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, bool status)
6905b9c547cSRui Paulo {
6915b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
6925b9c547cSRui Paulo 	sm->using_transmit_sa = status;
6935b9c547cSRui Paulo }
6945b9c547cSRui Paulo 
6955b9c547cSRui Paulo 
6965b9c547cSRui Paulo /**
6975b9c547cSRui Paulo  * ieee802_1x_cp_sm_step - Advance EAPOL state machines
6985b9c547cSRui Paulo  * @sm: EAPOL state machine
6995b9c547cSRui Paulo  *
7005b9c547cSRui Paulo  * This function is called to advance CP state machines after any change
7015b9c547cSRui Paulo  * that could affect their state.
7025b9c547cSRui Paulo  */
7035b9c547cSRui Paulo void ieee802_1x_cp_sm_step(void *cp_ctx)
7045b9c547cSRui Paulo {
7055b9c547cSRui Paulo 	/*
7065b9c547cSRui Paulo 	 * Run ieee802_1x_cp_step_run from a registered timeout
7075b9c547cSRui Paulo 	 * to make sure that other possible timeouts/events are processed
7085b9c547cSRui Paulo 	 * and to avoid long function call chains.
7095b9c547cSRui Paulo 	 */
7105b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = cp_ctx;
7115b9c547cSRui Paulo 	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
7125b9c547cSRui Paulo 	eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
7135b9c547cSRui Paulo }
7145b9c547cSRui Paulo 
7155b9c547cSRui Paulo 
7165b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
7175b9c547cSRui Paulo 					      void *timeout_ctx)
7185b9c547cSRui Paulo {
7195b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
7205b9c547cSRui Paulo 	sm->retire_when = 0;
7215b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
7225b9c547cSRui Paulo }
7235b9c547cSRui Paulo 
7245b9c547cSRui Paulo 
7255b9c547cSRui Paulo static void
7265b9c547cSRui Paulo ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
7275b9c547cSRui Paulo {
7285b9c547cSRui Paulo 	struct ieee802_1x_cp_sm *sm = eloop_ctx;
7295b9c547cSRui Paulo 	sm->transmit_when = 0;
7305b9c547cSRui Paulo 	ieee802_1x_cp_step_run(sm);
7315b9c547cSRui Paulo }
732