All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 08/10] dpp: add support for configuration protocol
@ 2021-12-14 18:12 James Prestwood
  0 siblings, 0 replies; only message in thread
From: James Prestwood @ 2021-12-14 18:12 UTC (permalink / raw)
  To: iwd

[-- Attachment #1: Type: text/plain, Size: 9946 bytes --]

This is a minimal implementation only supporting legacy network
configuration, i.e. only SSID and PSK/passphrase are supported.

Missing features include:
 - Fragmentation/comeback delay support
 - DPP AKM support
 - 8021x/PKEX support
---
 src/dpp.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 297 insertions(+)

diff --git a/src/dpp.c b/src/dpp.c
index 78074fd2..25cebfd3 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -45,6 +45,8 @@
 #include "src/crypto.h"
 #include "src/mpdu.h"
 #include "ell/useful.h"
+#include "src/common.h"
+#include "src/storage.h"
 
 static uint32_t netdev_watch;
 static struct l_genl_family *nl80211;
@@ -54,6 +56,7 @@ enum dpp_state {
 	DPP_STATE_NOTHING,
 	DPP_STATE_PRESENCE,
 	DPP_STATE_AUTHENTICATING,
+	DPP_STATE_CONFIGURING,
 };
 
 struct dpp_sm {
@@ -85,6 +88,7 @@ struct dpp_sm {
 	uint8_t auth_addr[6];
 	uint8_t r_nonce[32];
 	uint8_t i_nonce[32];
+	uint8_t e_nonce[32];
 
 	uint64_t ke[L_ECC_MAX_DIGITS];
 	uint64_t k2[L_ECC_MAX_DIGITS];
@@ -93,6 +97,8 @@ struct dpp_sm {
 	struct l_ecc_point *proto_public;
 
 	struct l_ecc_point *i_proto_public;
+
+	uint8_t diag_token;
 };
 
 static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data)
@@ -187,6 +193,13 @@ static size_t dpp_append_wrapped_data(uint8_t *hdr, uint8_t *wrap_start,
 		ad_size++;
 	}
 
+	/* The configuration result requires this zero-length data component */
+	if (hdr && hdr[5] == DPP_FRAME_CONFIGURATION_RESULT) {
+		ad[1].iov_base = NULL;
+		ad[1].iov_len = 0;
+		ad_size++;
+	}
+
 	va_start(va, num_attrs);
 
 	/* Count up total attributes length */
@@ -267,6 +280,284 @@ static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest,
 	return ptr - buf;
 }
 
+static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest,
+					uint8_t diag_token,
+					uint8_t buf[static 37])
+{
+	uint8_t *ptr = buf + 24;
+
+	memset(buf, 0, 37);
+
+	l_put_le16(0x00d0, buf);
+	memcpy(buf + 4, dest, 6);
+	memcpy(buf + 10, src, 6);
+	memcpy(buf + 16, broadcast, 6);
+
+	*ptr++ = 0x04; /* Public */
+	*ptr++ = 0x0a; /* Action */
+	*ptr++ = diag_token;
+
+	*ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL;
+	*ptr++ = 8; /* len */
+	*ptr++ = 0x00;
+	*ptr++ = IE_TYPE_VENDOR_SPECIFIC;
+	*ptr++ = 5;
+	memcpy(ptr, wifi_alliance_oui, 3);
+	ptr += 3;
+	*ptr++ = 0x1a;
+	*ptr++ = 1;
+
+	return ptr - buf;
+}
+
+static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr)
+{
+	const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\","
+				"\"netRole\":\"sta\"}";
+	struct iovec iov[3];
+	uint8_t hdr[37];
+	uint8_t attrs[512];
+	size_t json_len = strlen(json);
+	uint8_t *ptr = attrs;
+
+	l_getrandom(&dpp->diag_token, 1);
+
+	iov[0].iov_len = dpp_build_config_header(
+					netdev_get_address(dpp->netdev),
+					addr, dpp->diag_token, hdr);
+	iov[0].iov_base = hdr;
+
+	l_getrandom(dpp->e_nonce, dpp->nonce_len);
+
+	/* length */
+	ptr += 2;
+
+	ptr += dpp_append_wrapped_data(NULL, NULL, ptr, sizeof(attrs),
+			dpp->ke, dpp->key_len, 2,
+			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce,
+			DPP_ATTR_CONFIGURATION_REQUEST, json_len, json);
+
+	l_put_le16(ptr - attrs - 2, attrs);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = ptr - attrs;
+
+	dpp->state = DPP_STATE_CONFIGURING;
+
+	dpp_send_frame(dpp->wdev_id, iov, 2, dpp->current_freq);
+}
+
+static void send_config_result(struct dpp_sm *dpp, const uint8_t *to)
+{
+	uint8_t hdr[32];
+	struct iovec iov[2];
+	uint8_t attrs[256];
+	uint8_t *ptr = attrs;
+	uint8_t zero = 0;
+
+	iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), to,
+					DPP_FRAME_CONFIGURATION_RESULT, hdr);
+	iov[0].iov_base = hdr;
+
+	ptr += dpp_append_wrapped_data(hdr + 26, NULL, ptr, sizeof(attrs),
+			dpp->ke, dpp->key_len, 2,
+			DPP_ATTR_STATUS, 1, &zero,
+			DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce);
+
+	iov[1].iov_base = attrs;
+	iov[1].iov_len = ptr - attrs;
+
+	dpp_send_frame(dpp->wdev_id, iov, 2, dpp->current_freq);
+}
+
+static void dpp_write_config(struct dpp_configuration *config)
+{
+	_auto_(l_free) char *ssid = l_malloc(config->ssid_len + 1);
+	_auto_(l_settings_free) struct l_settings *settings = l_settings_new();
+	_auto_(l_free) char *path;
+
+	memcpy(ssid, config->ssid, config->ssid_len);
+	ssid[config->ssid_len] = '\0';
+
+	path = storage_get_network_file_path(SECURITY_PSK, ssid);
+
+	if (l_settings_load_from_file(settings, path)) {
+		/* Remove any existing Security keys */
+		l_settings_remove_group(settings, "Security");
+	}
+
+	if (config->passphrase)
+		l_settings_set_string(settings, "Security", "Passphrase",
+				config->passphrase);
+	else if (config->psk)
+		l_settings_set_string(settings, "Security", "PreSharedKey",
+				config->psk);
+
+	l_debug("Storing credential for '%s(%s)'", ssid,
+						security_to_str(SECURITY_PSK));
+	storage_network_sync(SECURITY_PSK, ssid, settings);
+}
+
+static void dpp_handle_config_frame(const struct mmpdu_header *frame,
+				const void *body, size_t body_len,
+				int rssi, void *user_data)
+{
+	struct dpp_sm *dpp = user_data;
+	const uint8_t *ptr = body;
+	uint16_t status;
+	uint16_t fragmented; /* Fragmented/Comeback delay field */
+	uint8_t adv_protocol_element[] = { 0x6C, 0x08, 0x7F };
+	uint8_t adv_protocol_id[] = { 0xDD, 0x05, 0x50, 0x6F,
+					0x9A, 0x1A, 0x01 };
+	uint16_t query_len;
+	struct dpp_attr_iter iter;
+	enum dpp_attribute_type type;
+	size_t len;
+	const uint8_t *data;
+	const char *json = NULL;
+	size_t json_len = 0;
+	int dstatus = -1;
+	const uint8_t *wrapped = NULL;
+	const uint8_t *e_nonce = NULL;
+	size_t wrapped_len = 0;
+	_auto_(l_free) uint8_t *unwrapped = NULL;
+	struct dpp_configuration *config;
+	uint8_t ad0[] = { 0x00, 0x10, 0x01, 0x00, 0x05 };
+	struct iovec ad = {
+		.iov_base = ad0,
+		.iov_len = 5
+	};
+
+	if (dpp->state != DPP_STATE_CONFIGURING)
+		return;
+
+	ptr += 2;
+
+	/*
+	 * Can a configuration request come from someone other than who you
+	 * authenticated to?
+	 */
+	if (memcmp(dpp->auth_addr, frame->address_2, 6))
+		return;
+
+	if (*ptr++ != dpp->diag_token)
+		return;
+
+	status = l_get_le16(ptr);
+	ptr += 2;
+
+	if (status != 0) {
+		l_debug("Bad configuration status %u", status);
+		return;
+	}
+
+	fragmented = l_get_le16(ptr);
+	ptr += 2;
+
+	/*
+	 * TODO: handle 0x0001 (fragmented), as well as comeback delay.
+	 */
+	if (fragmented != 0) {
+		l_debug("Fragmented messages not currently supported");
+		return;
+	}
+
+	if (memcmp(ptr, adv_protocol_element, sizeof(adv_protocol_element))) {
+		l_debug("Invalid Advertisement protocol element");
+		return;
+	}
+
+	ptr += sizeof(adv_protocol_element);
+
+	if (memcmp(ptr, adv_protocol_id, sizeof(adv_protocol_id))) {
+		l_debug("Invalid Advertisement protocol ID");
+		return;
+	}
+
+	ptr += sizeof(adv_protocol_id);
+
+	query_len = l_get_le16(ptr);
+	ptr += 2;
+
+	dpp_attr_iter_init(&iter, ptr, query_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_STATUS:
+			dstatus = l_get_u8(data);
+			break;
+		case DPP_ATTR_WRAPPED_DATA:
+			wrapped = data;
+			wrapped_len = len;
+			break;
+		default:
+			/*
+			 * TODO: CSR Attribute
+			 */
+			break;
+		}
+	}
+
+	if (dstatus != DPP_STATUS_OK || !wrapped) {
+		l_debug("Bad status or missing attributes");
+		return;
+	}
+
+	unwrapped = l_malloc(wrapped_len - 16);
+
+	aes_siv_decrypt(dpp->ke, dpp->key_len, wrapped, wrapped_len, &ad,
+				1, unwrapped);
+
+	wrapped_len -= 16;
+
+	dpp_attr_iter_init(&iter, unwrapped, wrapped_len);
+
+	while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
+		switch (type) {
+		case DPP_ATTR_ENROLLEE_NONCE:
+			if (len != dpp->nonce_len)
+				break;
+
+			if (memcmp(data, dpp->e_nonce, dpp->nonce_len))
+				break;
+
+			e_nonce = data;
+			break;
+		case DPP_ATTR_CONFIGURATION_OBJECT:
+			json = (const char *)data;
+			json_len = len;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!json || !e_nonce) {
+		l_debug("No configuration object in response");
+		return;
+	}
+
+	config = dpp_parse_configuration_object(json, json_len);
+	if (!config) {
+		l_error("Configuration object did not parse");
+		return;
+	}
+
+	dpp_write_config(config);
+	/*
+	 * TODO: Depending on the info included in the configuration object a
+	 * limited scan could be issued to get autoconnect to trigger faster.
+	 * In addition this network may already be in past scan results and
+	 * could be joined immediately.
+	 *
+	 * For now just wait for autoconnect.
+	 */
+
+	dpp_configuration_free(config);
+
+	send_config_result(dpp, dpp->auth_addr);
+}
+
 static void dpp_free_auth_data(struct dpp_sm *dpp)
 {
 	if (dpp->proto_public) {
@@ -457,6 +748,8 @@ static void authenticate_confirm(struct dpp_sm *dpp, const uint8_t *from,
 
 	l_debug("Authentication successful");
 
+	dpp_configuration_start(dpp, from);
+
 	return;
 
 auth_confirm_failed:
@@ -741,6 +1034,7 @@ static void dpp_create(struct netdev *netdev)
 	struct l_dbus *dbus = dbus_get_bus();
 	struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
 	uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 };
+	uint8_t dpp_conf_prefix[] = { 0x04, 0x0b };
 
 	dpp->netdev = netdev;
 	dpp->state = DPP_STATE_NOTHING;
@@ -752,6 +1046,9 @@ static void dpp_create(struct netdev *netdev)
 	frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0, dpp_prefix,
 				sizeof(dpp_prefix), dpp_handle_auth_frame,
 				dpp, NULL);
+	frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0, dpp_conf_prefix,
+				sizeof(dpp_conf_prefix),
+				dpp_handle_config_frame, dpp, NULL);
 }
 
 static void dpp_reset(struct dpp_sm *dpp)
-- 
2.31.1

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-14 18:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-14 18:12 [PATCH v2 08/10] dpp: add support for configuration protocol James Prestwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.