From ad51731abf06efb284d020578eb34e7b1daeb23e Mon Sep 17 00:00:00 2001
Message-Id: <ad51731abf06efb284d020578eb34e7b1daeb23e.1488376601.git.dcaratti@redhat.com>
From: Sabrina Dubroca <sd@queasysnail.net>
Date: Wed, 2 Nov 2016 16:38:35 +0100
Subject: [PATCH] wpa_supplicant: Allow pre-shared (CAK,CKN) pair for MKA

This enables configuring key_mgmt=NONE + mka_ckn + mka_cak.
This allows wpa_supplicant to work in a peer-to-peer mode, where peers
are authenticated by the pre-shared (CAK,CKN) pair. In this mode, peers
can act as key server to distribute keys for the MACsec instances.

This is what some MACsec switches support, and even without HW
support, it's a convenient way to setup a network.

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
---
 wpa_supplicant/config.c            | 65 ++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/config_file.c       | 36 +++++++++++++++++++++
 wpa_supplicant/config_ssid.h       | 20 ++++++++++++
 wpa_supplicant/wpa_supplicant.c    |  7 +++-
 wpa_supplicant/wpa_supplicant.conf |  8 +++++
 wpa_supplicant/wpas_kay.c          | 48 ++++++++++++++++++++++++++++
 wpa_supplicant/wpas_kay.h          | 10 ++++++
 7 files changed, 193 insertions(+), 1 deletion(-)

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index a0b64b2..9011389 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1828,6 +1828,69 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
 #endif /* CONFIG_MESH */
 
 
+#ifdef CONFIG_MACSEC
+
+static int wpa_config_parse_mka_cak(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	if (hexstr2bin(value, ssid->mka_cak, MACSEC_CAK_LEN) ||
+	    value[MACSEC_CAK_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
+			   line, value);
+		return -1;
+	}
+
+	ssid->mka_psk_set |= MKA_PSK_SET_CAK;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, MACSEC_CAK_LEN);
+	return 0;
+}
+
+
+static int wpa_config_parse_mka_ckn(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	if (hexstr2bin(value, ssid->mka_ckn, MACSEC_CKN_LEN) ||
+	    value[MACSEC_CKN_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+			   line, value);
+		return -1;
+	}
+
+	ssid->mka_psk_set |= MKA_PSK_SET_CKN;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, MACSEC_CKN_LEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mka_cak(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+		return NULL;
+
+	return wpa_config_write_string_hex(ssid->mka_cak, MACSEC_CAK_LEN);
+}
+
+
+static char * wpa_config_write_mka_ckn(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+		return NULL;
+	return wpa_config_write_string_hex(ssid->mka_ckn, MACSEC_CKN_LEN);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MACSEC */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -2062,6 +2125,8 @@ static const struct parse_data ssid_fields[] = {
 	{ INT(beacon_int) },
 #ifdef CONFIG_MACSEC
 	{ INT_RANGE(macsec_policy, 0, 1) },
+	{ FUNC_KEY(mka_cak) },
+	{ FUNC_KEY(mka_ckn) },
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	{ INT(update_identifier) },
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 7ae1654..172508e 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -662,6 +662,40 @@ static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
 #endif /* CONFIG_P2P */
 
 
+#ifdef CONFIG_MACSEC
+
+static void write_mka_cak(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+		return;
+
+	value = wpa_config_get(ssid, "mka_cak");
+	if (!value)
+		return;
+	fprintf(f, "\tmka_cak=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+		return;
+
+	value = wpa_config_get(ssid, "mka_ckn");
+	if (!value)
+		return;
+	fprintf(f, "\tmka_ckn=%s\n", value);
+	os_free(value);
+}
+
+#endif /* CONFIG_MACSEC */
+
+
 static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 {
 	int i;
@@ -772,6 +806,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 	INT(beacon_int);
 #ifdef CONFIG_MACSEC
 	INT(macsec_policy);
+	write_mka_cak(f, ssid);
+	write_mka_ckn(f, ssid);
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	INT(update_identifier);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 010b594..a530cda 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -728,6 +728,26 @@ struct wpa_ssid {
 	 *    determine whether to use a secure session or not.
 	 */
 	int macsec_policy;
+
+	/**
+	 * mka_ckn - MKA pre-shared CKN
+	 */
+#define MACSEC_CKN_LEN 32
+	u8 mka_ckn[MACSEC_CKN_LEN];
+
+	/**
+	 * mka_cak - MKA pre-shared CAK
+	 */
+#define MACSEC_CAK_LEN 16
+	u8 mka_cak[MACSEC_CAK_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+	/**
+	 * mka_psk_set - Whether mka_ckn and mka_cak are set
+	 */
+	u8 mka_psk_set;
 #endif /* CONFIG_MACSEC */
 
 #ifdef CONFIG_HS20
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 5d6326a..0bfc39d 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -329,7 +329,12 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
 
 	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
 
-	ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#ifdef CONFIG_MACSEC
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set)
+		ieee802_1x_create_preshared_mka(wpa_s, ssid);
+	else
+		ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* CONFIG_MACSEC */
 #endif /* IEEE8021X_EAPOL */
 }
 
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 047ca90..8fa740b 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -892,6 +892,14 @@ fast_reauth=1
 # 1: MACsec enabled - Should secure, accept key server's advice to
 #    determine whether to use a secure session or not.
 #
+# mka_cak and mka_ckn: IEEE 802.1X/MACsec pre-shared authentication mode
+# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
+# In this mode, instances of wpa_supplicant can act as peers, one of
+# which will become the key server and start distributing SAKs.
+# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-bytes (128 bit)
+# hex-string (32 hex-digits)
+# mka_ckn (CKN = CAK Name) takes a 32-bytes (256 bit) hex-string (64 hex-digits)
+#
 # mixed_cell: This option can be used to configure whether so called mixed
 # cells, i.e., networks that use both plaintext and encryption in the same
 # SSID, are allowed when selecting a BSS from scan results.
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index e032330..80b98d9 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -371,3 +371,51 @@ fail:
 
 	return res;
 }
+
+
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid)
+{
+	struct mka_key *cak;
+	struct mka_key_name *ckn;
+	void *res;
+
+	if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+		return NULL;
+
+	if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0)
+		return NULL;
+
+	if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	ckn = os_zalloc(sizeof(*ckn));
+	if (!ckn)
+		goto dealloc;
+
+	cak = os_zalloc(sizeof(*cak));
+	if (!cak)
+		goto free_ckn;
+
+	cak->len = MACSEC_CAK_LEN;
+	os_memcpy(cak->key, ssid->mka_cak, cak->len);
+
+	ckn->len = MACSEC_CKN_LEN;
+	os_memcpy(ckn->name, ssid->mka_ckn, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE);
+	if (res)
+		return res;
+
+	/* Failed to create MKA */
+	os_free(cak);
+
+	/* fallthrough */
+
+free_ckn:
+	os_free(ckn);
+dealloc:
+	ieee802_1x_dealloc_kay_sm(wpa_s);
+
+	return NULL;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
index b7236d0..81f8e0c 100644
--- a/wpa_supplicant/wpas_kay.h
+++ b/wpa_supplicant/wpas_kay.h
@@ -17,6 +17,9 @@ void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
 				      const u8 *peer_addr);
 void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
 
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid);
+
 #else /* CONFIG_MACSEC */
 
 static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
@@ -36,6 +39,13 @@ static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
 {
 }
 
+static inline void *
+ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
 #endif /* CONFIG_MACSEC */
 
 #endif /* WPAS_KAY_H */
-- 
2.7.4

