This implements internal key storage add/delete/fetch for the three basic key types managed in Mesh: Network, Application and Device. This key storage is separate from keys assigned to nodes within the mesh, and are used to support Configuration Client functionality. --- Makefile.mesh | 1 + mesh/README | 35 ++++++- mesh/keyring.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mesh/keyring.h | 49 +++++++++ mesh/node.c | 17 +++- mesh/node.h | 2 + mesh/storage.c | 95 ++++++++++++----- mesh/storage.h | 2 + 8 files changed, 485 insertions(+), 31 deletions(-) create mode 100644 mesh/keyring.c create mode 100644 mesh/keyring.h diff --git a/Makefile.mesh b/Makefile.mesh index 61fb40936..cefb72aed 100644 --- a/Makefile.mesh +++ b/Makefile.mesh @@ -25,6 +25,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \ mesh/agent.h mesh/agent.c \ mesh/prov-acceptor.c mesh/prov-initiator.c \ mesh/pb-adv.h mesh/pb-adv.c \ + mesh/keyring.h mesh/keyring.c \ mesh/mesh-defs.h libexec_PROGRAMS += mesh/bluetooth-meshd diff --git a/mesh/README b/mesh/README index 151a1e6a1..029e45068 100644 --- a/mesh/README +++ b/mesh/README @@ -37,8 +37,39 @@ Each subdirectory contains the following files: provisioner/configuration client - node.json.bak: a backup that the last known good node configuration. + - seq_num: + File containing next sequence number to use + - seq_num.bak: + Backup of the Sequence number. This may be larger than the + actual sequence number being used at runtime, to prevent re-use of sequence numbers in the event of an unexpected restart. + - ./rpl/: + Directory to store the sequence numbers of remote nodes, as + required by Replay Protection List (RPL) parameters. + - xxxx: + Files named for remote Unicast addresses, and contain + last received iv_index + seq_num from each SRC address. + - ./dev_keys/: + Directory to store remote Device keys. This is only created/used + by Configuration Client (Network administration) nodes. + - xxxx: + Files named for remote Unicast addresses, and contains + 16 octet key. + - ./net_keys/: + Directory to store network subnet keys. This is only + created/used by Configuration Client (Network administration) + nodes. + - xxx: + Files named for subnet index, and contains key refresh + phase, and old/new versions of the key. + - ./app_keys/: + Directory to store application keys. This is only created/used + by Configuration Client (Network administration) nodes. + - xxx: + Files named for application index, and contains bound + subnet index, and old/new versions of the key. -The files are in JSON format. +The node.json and node.json.bak are in JSON format. All other files are stored +in little endian binary format. Information =========== @@ -47,4 +78,4 @@ Mailing lists: linux-bluetooth@xxxxxxxxxxxxxxx For additional information about the project visit BlueZ web site: - http://www.bluez.org \ No newline at end of file + http://www.bluez.org diff --git a/mesh/keyring.c b/mesh/keyring.c new file mode 100644 index 000000000..6fca047fd --- /dev/null +++ b/mesh/keyring.c @@ -0,0 +1,315 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> +#include <libgen.h> + +#include <ell/ell.h> + +#include "mesh/mesh-defs.h" + +#include "mesh/net.h" +#include "mesh/storage.h" +#include "mesh/keyring.h" +#include "mesh/mesh.h" +#include "mesh/node.h" + +const char *dev_key_dir = "dev_keys"; +const char *app_key_dir = "app_keys"; +const char *net_key_dir = "net_keys"; + +bool keyring_put_net_key(struct mesh_node *node, uint16_t net_idx, + struct keyring_net_key *key) +{ + char *node_path; + char *key_file; + size_t len; + int fd; + bool result = false; + + if (!node || !key) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(net_key_dir) + 2 + 4; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s", node_path, net_key_dir); + storage_create_dir(key_file); + snprintf(key_file, len, "%s/%s/%3.3x", node_path, net_key_dir, net_idx); + l_debug("Put Net Key %s", key_file); + + fd = open(key_file, O_WRONLY | O_CREAT | O_TRUNC); + if (fd) { + if (write(fd, key, sizeof(*key)) == sizeof(*key)) + result = true; + + close(fd); + } + + l_free(key_file); + return result; +} + +bool keyring_put_app_key(struct mesh_node *node, uint16_t app_idx, + uint16_t net_idx, struct keyring_app_key *key) +{ + char *node_path; + char *key_file; + size_t len; + int fd; + bool result = false; + + if (!node || !key) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(app_key_dir) + 2 + 4; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s", node_path, app_key_dir); + storage_create_dir(key_file); + snprintf(key_file, len, "%s/%s/%3.3x", node_path, app_key_dir, app_idx); + l_debug("Put App Key %s", key_file); + + fd = open(key_file, O_RDONLY); + if (fd) { + struct keyring_app_key old_key; + + if (read(fd, &old_key, sizeof(*key)) == sizeof(*key)) { + if (old_key.net_idx != net_idx) { + close(fd); + goto done; + } + } + close(fd); + } + + fd = open(key_file, O_WRONLY | O_CREAT | O_TRUNC); + if (write(fd, key, sizeof(*key)) == sizeof(*key)) + result = true; + + close(fd); + +done: + l_free(key_file); + return result; +} + +bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast, + uint8_t count, uint8_t dev_key[16]) +{ + char *node_path; + char *key_file; + size_t len; + int fd, i; + bool result = true; + + if (!node) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(dev_key_dir) + 2 + 5; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s", node_path, dev_key_dir); + storage_create_dir(key_file); + + for (i = 0; i < count; i++) { + snprintf(key_file, len, "%s/%s/%4.4x", node_path, dev_key_dir, + unicast + i); + l_debug("Put Dev Key %s", key_file); + + fd = open(key_file, O_WRONLY | O_CREAT | O_TRUNC); + if (fd) { + if (write(fd, dev_key, 16) != 16) + result = false; + + close(fd); + } else + result = false; + } + + l_free(key_file); + return result; +} + +bool keyring_get_net_key(struct mesh_node *node, uint16_t net_idx, + struct keyring_net_key *key) +{ + char *node_path; + char *key_file; + size_t len; + int fd; + bool result = false; + + if (!node || !key) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(net_key_dir) + 2 + 4; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s/%3.3x", node_path, net_key_dir, net_idx); + + fd = open(key_file, O_RDONLY); + if (fd) { + if (read(fd, key, sizeof(*key)) == sizeof(*key)) + result = true; + + close(fd); + } + + l_free(key_file); + return result; +} + +bool keyring_get_app_key(struct mesh_node *node, uint16_t app_idx, + struct keyring_app_key *key) +{ + char *node_path; + char *key_file; + size_t len; + int fd; + bool result = false; + + if (!node || !key) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(app_key_dir) + 2 + 4; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s/%3.3x", node_path, app_key_dir, app_idx); + + fd = open(key_file, O_RDONLY); + if (fd) { + if (read(fd, key, sizeof(*key)) == sizeof(*key)) + result = true; + + close(fd); + } + + l_free(key_file); + return result; +} + +bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast, + uint8_t dev_key[16]) +{ + char *node_path; + char *key_file; + size_t len; + int fd; + bool result = false; + + if (!node) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(dev_key_dir) + 2 + 5; + key_file = l_malloc(len); + snprintf(key_file, len, "%s/%s/%4.4x", node_path, dev_key_dir, unicast); + + fd = open(key_file, O_RDONLY); + if (fd) { + if (read(fd, dev_key, 16) == 16) + result = true; + + close(fd); + } + + l_free(key_file); + return result; +} + +bool keyring_del_net_key(struct mesh_node *node, uint16_t net_idx) +{ + char *node_path; + char *key_file; + size_t len; + + if (!node) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(net_key_dir) + 2 + 4; + key_file = l_malloc(len); + + snprintf(key_file, len, "%s/%s/%3.3x", node_path, net_key_dir, net_idx); + l_debug("RM Net Key %s", key_file); + remove(key_file); + + /* TODO: See if it is easiest to delete all bound App keys here */ + /* TODO: see nftw() */ + + l_free(key_file); + return true; +} + +bool keyring_del_app_key(struct mesh_node *node, uint16_t app_idx) +{ + char *node_path; + char *key_file; + size_t len; + + if (!node) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(app_key_dir) + 2 + 4; + key_file = l_malloc(len); + + snprintf(key_file, len, "%s/%s/%3.3x", node_path, app_key_dir, app_idx); + l_debug("RM App Key %s", key_file); + remove(key_file); + + l_free(key_file); + return true; +} + +bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast, + uint8_t count) +{ + char *node_path; + char *key_file; + size_t len; + int i; + + if (!node) + return false; + + node_path = storage_path_get(node); + len = strlen(node_path) + strlen(dev_key_dir) + 2 + 5; + key_file = l_malloc(len); + + for (i = 0; i < count; i++) { + snprintf(key_file, len, "%s/%s/%4.4x", node_path, dev_key_dir, + unicast + i); + l_debug("RM Dev Key %s", key_file); + remove(key_file); + } + + l_free(key_file); + return true; +} diff --git a/mesh/keyring.h b/mesh/keyring.h new file mode 100644 index 000000000..167191013 --- /dev/null +++ b/mesh/keyring.h @@ -0,0 +1,49 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +struct keyring_net_key { + uint16_t net_idx; + uint8_t phase; + uint8_t old_key[16]; + uint8_t new_key[16]; +}; + +struct keyring_app_key { + uint16_t app_idx; + uint16_t net_idx; + uint8_t old_key[16]; + uint8_t new_key[16]; +}; + +bool keyring_put_net_key(struct mesh_node *node, uint16_t net_idx, + struct keyring_net_key *key); +bool keyring_get_net_key(struct mesh_node *node, uint16_t net_idx, + struct keyring_net_key *key); +bool keyring_del_net_key(struct mesh_node *node, uint16_t net_idx); +bool keyring_put_app_key(struct mesh_node *node, uint16_t app_idx, + uint16_t net_idx, struct keyring_app_key *key); +bool keyring_get_app_key(struct mesh_node *node, uint16_t app_idx, + struct keyring_app_key *key); +bool keyring_del_app_key(struct mesh_node *node, uint16_t app_idx); +bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast, + uint8_t dev_key[16]); +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); diff --git a/mesh/node.c b/mesh/node.c index 774d03d45..dc09a11f7 100644 --- a/mesh/node.c +++ b/mesh/node.c @@ -79,6 +79,7 @@ struct mesh_node { char *path; void *jconfig; char *cfg_file; + char *node_path; uint32_t disc_watch; time_t upd_sec; uint32_t seq_number; @@ -235,6 +236,8 @@ static void free_node_resources(void *data) l_free(node->comp); l_free(node->app_path); l_free(node->owner); + l_free(node->cfg_file); + l_free(node->node_path); if (node->disc_watch) l_dbus_remove_watch(dbus_get_bus(), node->disc_watch); @@ -1875,7 +1878,8 @@ void *node_jconfig_get(struct mesh_node *node) void node_cfg_file_set(struct mesh_node *node, char *cfg) { - node->cfg_file = cfg; + l_free(node->cfg_file); + node->cfg_file = l_strdup(cfg); } char *node_cfg_file_get(struct mesh_node *node) @@ -1883,6 +1887,17 @@ char *node_cfg_file_get(struct mesh_node *node) return node->cfg_file; } +void node_path_set(struct mesh_node *node, char *path) +{ + l_free(node->node_path); + node->node_path = l_strdup(path); +} + +char *node_path_get(struct mesh_node *node) +{ + return node->node_path; +} + struct mesh_net *node_get_net(struct mesh_node *node) { return node->net; diff --git a/mesh/node.h b/mesh/node.h index 20b60099e..d6a4cd96c 100644 --- a/mesh/node.h +++ b/mesh/node.h @@ -97,3 +97,5 @@ void node_jconfig_set(struct mesh_node *node, void *jconfig); void *node_jconfig_get(struct mesh_node *node); void node_cfg_file_set(struct mesh_node *node, char *cfg); char *node_cfg_file_get(struct mesh_node *node); +void node_path_set(struct mesh_node *node, char *path); +char *node_path_get(struct mesh_node *node); diff --git a/mesh/storage.c b/mesh/storage.c index 8a70b5696..799ed4734 100644 --- a/mesh/storage.c +++ b/mesh/storage.c @@ -28,6 +28,7 @@ #include <unistd.h> #include <dirent.h> #include <libgen.h> +#include <ftw.h> #include <sys/types.h> #include <sys/stat.h> @@ -61,6 +62,20 @@ static bool simple_match(const void *a, const void *b) return a == b; } +/* This is a thread-safe always malloced version of dirname which will work + * regardless of which underlying dirname() implementation is used. + */ +static char *alloc_dirname(const char *path) +{ + char *tmp = l_strdup(path); + char *dir; + + dir = dirname(tmp); + strncpy(tmp, dir, strlen(path) + 1); + + return tmp; +} + static bool read_node_cb(struct mesh_db_node *db_node, void *user_data) { struct mesh_node *node = user_data; @@ -483,7 +498,7 @@ void storage_save_config(struct mesh_node *node, bool no_wait, l_idle_oneshot(idle_save_config, info, NULL); } -static int create_dir(const char *dirname) +int storage_create_dir(const char *dirname) { struct stat st; char dir[PATH_MAX + 1], *prev, *next; @@ -524,7 +539,7 @@ bool storage_load_nodes(const char *dir_name) DIR *dir; struct dirent *entry; - create_dir(dir_name); + storage_create_dir(dir_name); dir = opendir(dir_name); if (!dir) { l_error("Failed to open mesh node storage directory: %s", @@ -634,15 +649,50 @@ fail: return false; } +char *storage_path_get(struct mesh_node *node) +{ + char *node_path = node_path_get(node); + + if (!node_path) { + char *cfgname = (char *) node_cfg_file_get(node); + + if (!cfgname) + return NULL; + + node_path = alloc_dirname(cfgname); + node_path_set(node, node_path); + l_free(node_path); + + return node_path_get(node); + } + + return node_path; +} + +static int del_fobject(const char *fpath, const struct stat *sb, int typeflag, + struct FTW *ftwbuf) +{ + switch (typeflag) { + case FTW_DP: + rmdir(fpath); + l_debug("RMDIR %s", fpath); + break; + + case FTW_SL: + default: + remove(fpath); + l_debug("RM %s", fpath); + break; + } + return 0; +} + /* Permanently remove node configuration */ void storage_remove_node_config(struct mesh_node *node) { - char *cfgname; + char *node_path, *mesh_path, *mesh_name; struct json_object *jnode; - const char *dir_name; uint16_t node_id; - size_t len; - char *bak; if (!node) return; @@ -653,31 +703,20 @@ void storage_remove_node_config(struct mesh_node *node) json_object_put(jnode); node_jconfig_set(node, NULL); - /* Delete node configuration file */ - cfgname = (char *) node_cfg_file_get(node); - if (!cfgname) - return; - - l_debug("Delete node config file %s", cfgname); - remove(cfgname); + node_path = storage_path_get(node); + l_debug("Delete node config %s", node_path); - /* Delete the backup file */ - len = strlen(cfgname) + 5; - bak = l_malloc(len); - strncpy(bak, cfgname, len); - bak = strncat(bak, ".bak", 5); - remove(bak); - l_free(bak); - - /* Delete the node directory */ - dir_name = dirname(cfgname); - - l_debug("Delete directory %s", dir_name); - rmdir(dir_name); + /* Make sure path name of node follows expected guidelines */ + mesh_path = alloc_dirname(node_path); + mesh_name = basename(mesh_path); + if (strcmp(mesh_name, "mesh")) + goto done; - l_free(cfgname); - node_cfg_file_set(node, NULL); + nftw(node_path, del_fobject, 5, FTW_DEPTH | FTW_PHYS); node_id = node_id_get(node); l_queue_remove(node_ids, L_UINT_TO_PTR(node_id)); + +done: + l_free(mesh_path); } diff --git a/mesh/storage.h b/mesh/storage.h index d71d11b8e..4cfeaa7f4 100644 --- a/mesh/storage.h +++ b/mesh/storage.h @@ -49,3 +49,5 @@ bool storage_set_device_key(struct mesh_node *node, uint8_t dev_key[16]); bool storage_set_unicast(struct mesh_node *node, uint16_t unicast); bool storage_set_key_refresh_phase(struct mesh_net *net, uint16_t net_idx, uint8_t phase); +int storage_create_dir(const char *dirname); +char *storage_path_get(struct mesh_node *node); -- 2.14.5