Diff between 1dc110689577ddd14c44b1c869f695856381d03f and eb642a59fbeb72d6f4580eeb7f0cbf6759720b8a

Changed Files

File Additions Deletions Status
mesh/keyring.c +285 -1 modified
mesh/keyring.h +2 -0 modified
mesh/manager.c +35 -0 modified

Full Patch

diff --git a/mesh/keyring.c b/mesh/keyring.c
index f27fe42..4b90164 100644
--- a/mesh/keyring.c
+++ b/mesh/keyring.c
@@ -26,6 +26,7 @@
 
 #include "mesh/mesh-defs.h"
 
+#include "mesh/dbus.h"
 #include "mesh/node.h"
 #include "mesh/keyring.h"
 
@@ -276,7 +277,6 @@ bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 
 	snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path, dev_key_dir,
 								unicast);
-
 	fd = open(key_file, O_RDONLY);
 	if (fd >= 0) {
 		if (read(fd, dev_key, 16) == 16)
@@ -349,3 +349,287 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 
 	return true;
 }
+
+static DIR *open_key_dir(const char *node_path, const char *key_dir_name)
+{
+	char dir_path[PATH_MAX];
+	DIR *key_dir;
+
+	if (strlen(node_path) + strlen(key_dir_name) + 1 >= PATH_MAX)
+		return NULL;
+
+	snprintf(dir_path, PATH_MAX, "%s%s", node_path, key_dir_name);
+
+	key_dir = opendir(dir_path);
+	if (!key_dir) {
+		l_error("Failed to open keyring storage directory: %s",
+								dir_path);
+		return NULL;
+	}
+
+	return key_dir;
+}
+
+static int open_key_dir_entry(int dir_fd, struct dirent *entry,
+							uint8_t fname_len)
+{
+	if (entry->d_type != DT_REG)
+		return -1;
+
+	/* Check the file name length */
+	if (strlen(entry->d_name) != fname_len)
+		return -1;
+
+	return openat(dir_fd, entry->d_name, O_RDONLY);
+}
+
+static void append_old_key(struct l_dbus_message_builder *builder,
+							const uint8_t key[16])
+{
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "OldKey");
+	l_dbus_message_builder_enter_variant(builder, "ay");
+	dbus_append_byte_array(builder, key, 16);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static void build_app_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder,
+					uint16_t net_idx, uint8_t phase)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+
+	key_dir = open_key_dir(node_path, app_key_dir);
+	if (!key_dir)
+		return;
+
+	key_dir_fd = dirfd(key_dir);
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "AppKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})");
+	l_dbus_message_builder_enter_array(builder, "(qaya{sv})");
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		struct keyring_app_key key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 3);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, &key, sizeof(key)) != sizeof(key) ||
+						key.net_idx != net_idx) {
+			close(fd);
+			continue;
+		}
+
+		close(fd);
+
+		l_dbus_message_builder_enter_struct(builder, "qaya{sv}");
+
+		l_dbus_message_builder_append_basic(builder, 'q', &key.app_idx);
+		dbus_append_byte_array(builder, key.new_key, 16);
+
+		l_dbus_message_builder_enter_array(builder, "{sv}");
+
+		if (phase != KEY_REFRESH_PHASE_NONE)
+			append_old_key(builder, key.old_key);
+
+		l_dbus_message_builder_leave_array(builder);
+		l_dbus_message_builder_leave_struct(builder);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	closedir(key_dir);
+}
+
+static bool build_net_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+	bool result = false;
+
+	key_dir = open_key_dir(node_path, net_key_dir);
+	if (!key_dir)
+		return false;
+
+	key_dir_fd = dirfd(key_dir);
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "NetKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qaya{sv})");
+	l_dbus_message_builder_enter_array(builder, "(qaya{sv})");
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		struct keyring_net_key key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 3);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, &key, sizeof(key)) != sizeof(key)) {
+			close(fd);
+			goto done;
+		}
+
+		close(fd);
+
+		/*
+		 * If network key is stuck in phase 3, keyring
+		 * write failed and this key info is unreliable.
+		 */
+		if (key.phase == KEY_REFRESH_PHASE_THREE)
+			continue;
+
+		l_dbus_message_builder_enter_struct(builder, "qaya{sv}");
+
+		l_dbus_message_builder_append_basic(builder, 'q', &key.net_idx);
+		dbus_append_byte_array(builder, key.new_key, 16);
+
+		l_dbus_message_builder_enter_array(builder, "{sv}");
+
+		if (key.phase != KEY_REFRESH_PHASE_NONE) {
+			dbus_append_dict_entry_basic(builder, "Phase", "y",
+								&key.phase);
+			append_old_key(builder, key.old_key);
+		}
+
+		build_app_keys_reply(node_path, builder, key.net_idx,
+								key.phase);
+
+		l_dbus_message_builder_leave_array(builder);
+		l_dbus_message_builder_leave_struct(builder);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	result = true;
+done:
+	closedir(key_dir);
+
+	return result;
+
+}
+
+struct dev_key_entry {
+	uint16_t unicast;
+	uint8_t value[16];
+};
+
+static bool match_key_value(const void *a, const void *b)
+{
+	const struct dev_key_entry *key = a;
+	const uint8_t *value = b;
+
+	return (memcmp(key->value, value, 16) == 0);
+}
+
+static void build_dev_key_entry(void *a, void *b)
+{
+	struct dev_key_entry *key = a;
+	struct l_dbus_message_builder *builder = b;
+
+	l_dbus_message_builder_enter_struct(builder, "qay");
+	l_dbus_message_builder_append_basic(builder, 'q', &key->unicast);
+	dbus_append_byte_array(builder, key->value, 16);
+	l_dbus_message_builder_leave_struct(builder);
+}
+
+static bool build_dev_keys_reply(const char *node_path,
+					struct l_dbus_message_builder *builder)
+{
+	DIR *key_dir;
+	int key_dir_fd;
+	struct dirent *entry;
+	struct l_queue *keys;
+	bool result = false;
+
+	key_dir = open_key_dir(node_path, dev_key_dir);
+	/*
+	 * There is always at least one device key present for a local node.
+	 * Therefore, return false, if the directory does not exist.
+	 */
+	if (!key_dir)
+		return false;
+
+	key_dir_fd = dirfd(key_dir);
+
+	keys = l_queue_new();
+
+	while ((entry = readdir(key_dir)) != NULL) {
+		uint8_t buf[16];
+		uint16_t unicast;
+		struct dev_key_entry *key;
+		int fd = open_key_dir_entry(key_dir_fd, entry, 4);
+
+		if (fd < 0)
+			continue;
+
+		if (read(fd, buf, 16) != 16) {
+			close(fd);
+			goto done;
+		}
+
+		close(fd);
+
+		if (sscanf(entry->d_name, "%04hx", &unicast) != 1)
+			goto done;
+
+		key = l_queue_find(keys, match_key_value, buf);
+
+		if (key) {
+			if (key->unicast > unicast)
+				key->unicast = unicast;
+			continue;
+		}
+
+		key = l_new(struct dev_key_entry, 1);
+		key->unicast = unicast;
+		memcpy(key->value, buf, 16);
+		l_queue_push_tail(keys, key);
+	}
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', "DevKeys");
+	l_dbus_message_builder_enter_variant(builder, "a(qay)");
+	l_dbus_message_builder_enter_array(builder, "(qay)");
+
+	l_queue_foreach(keys, build_dev_key_entry, builder);
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	result = true;
+done:
+	l_queue_destroy(keys, l_free);
+	closedir(key_dir);
+
+	return result;
+}
+
+bool keyring_build_export_keys_reply(struct mesh_node *node,
+					struct l_dbus_message_builder *builder)
+{
+	const char *node_path;
+
+	if (!node)
+		return false;
+
+	node_path = node_get_storage_dir(node);
+
+	if (!build_net_keys_reply(node_path, builder))
+		return false;
+
+	return build_dev_keys_reply(node_path, builder);
+}
diff --git a/mesh/keyring.h b/mesh/keyring.h
index c2d38e9..ecf62cb 100644
--- a/mesh/keyring.h
+++ b/mesh/keyring.h
@@ -39,3 +39,5 @@ bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 					uint8_t count, uint8_t dev_key[16]);
 bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
 								uint8_t count);
+bool keyring_build_export_keys_reply(struct mesh_node *node,
+					struct l_dbus_message_builder *builder);
diff --git a/mesh/manager.c b/mesh/manager.c
index d70993e..e66b1a4 100644
--- a/mesh/manager.c
+++ b/mesh/manager.c
@@ -776,6 +776,38 @@ static struct l_dbus_message *set_key_phase_call(struct l_dbus *dbus,
 	return l_dbus_message_new_method_return(msg);
 }
 
+static struct l_dbus_message *export_keys_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	const char *sender = l_dbus_message_get_sender(msg);
+	struct l_dbus_message_builder *builder;
+	struct l_dbus_message *reply;
+	struct mesh_node *node = user_data;
+
+	l_debug("Export Keys");
+
+	if (strcmp(sender, node_get_owner(node)))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	builder = l_dbus_message_builder_new(reply);
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	if (!keyring_build_export_keys_reply(node, builder)) {
+		l_dbus_message_builder_destroy(builder);
+		l_dbus_message_unref(reply);
+		return dbus_error(msg, MESH_ERROR_FAILED, NULL);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	return reply;
+}
+
 static void setup_management_interface(struct l_dbus_interface *iface)
 {
 	l_dbus_interface_method(iface, "AddNode", 0, add_node_call, "",
@@ -807,6 +839,9 @@ static void setup_management_interface(struct l_dbus_interface *iface)
 							"app_index", "app_key");
 	l_dbus_interface_method(iface, "SetKeyPhase", 0, set_key_phase_call, "",
 						"qy", "net_index", "phase");
+	l_dbus_interface_method(iface, "ExportKeys", 0, export_keys_call,
+							"a(qaya{sv})a(qay)", "",
+							"net_keys", "dev_keys");
 }
 
 bool manager_dbus_init(struct l_dbus *bus)