| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- // SPDX-License-Identifier: LGPL-2.1-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2019 Intel Corporation. All rights reserved.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <fcntl.h>
- #include <dirent.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <ell/ell.h>
- #include "mesh/mesh-defs.h"
- #include "mesh/dbus.h"
- #include "mesh/node.h"
- #include "mesh/keyring.h"
- const char *dev_key_dir = "/dev_keys";
- const char *app_key_dir = "/app_keys";
- const char *net_key_dir = "/net_keys";
- static int open_key_file(struct mesh_node *node, const char *key_dir,
- uint16_t idx, int flags)
- {
- const char *node_path;
- char fname[PATH_MAX];
- if (!node)
- return -1;
- node_path = node_get_storage_dir(node);
- if (strlen(node_path) + strlen(key_dir) + 1 + 3 >= PATH_MAX)
- return -1;
- if (flags & O_CREAT) {
- snprintf(fname, PATH_MAX, "%s%s", node_path, key_dir);
- mkdir(fname, 0755);
- }
- snprintf(fname, PATH_MAX, "%s%s/%3.3x", node_path, key_dir, idx);
- if (flags & O_CREAT)
- return open(fname, flags, 0600);
- else
- return open(fname, flags);
- }
- bool keyring_put_net_key(struct mesh_node *node, uint16_t net_idx,
- struct keyring_net_key *key)
- {
- bool result = false;
- int fd;
- if (!key)
- return false;
- fd = open_key_file(node, net_key_dir, net_idx,
- O_WRONLY | O_CREAT | O_TRUNC);
- if (fd < 0)
- return false;
- if (write(fd, key, sizeof(*key)) == sizeof(*key))
- result = true;
- close(fd);
- return result;
- }
- bool keyring_put_app_key(struct mesh_node *node, uint16_t app_idx,
- uint16_t net_idx, struct keyring_app_key *key)
- {
- bool result = false;
- int fd;
- if (!key)
- return false;
- fd = open_key_file(node, app_key_dir, app_idx, O_RDWR);
- if (fd >= 0) {
- struct keyring_app_key old_key;
- if (read(fd, &old_key, sizeof(old_key)) == sizeof(old_key)) {
- if (old_key.net_idx != net_idx) {
- close(fd);
- return false;
- }
- }
- lseek(fd, 0, SEEK_SET);
- } else
- fd = open_key_file(node, app_key_dir, app_idx,
- O_WRONLY | O_CREAT | O_TRUNC);
- if (fd < 0)
- return false;
- if (write(fd, key, sizeof(*key)) == sizeof(*key))
- result = true;
- close(fd);
- return result;
- }
- static void finalize(int dir_fd, const char *fname, uint16_t net_idx)
- {
- struct keyring_app_key key;
- int fd;
- fd = openat(dir_fd, fname, O_RDWR);
- if (fd < 0)
- return;
- if (read(fd, &key, sizeof(key)) != sizeof(key) ||
- key.net_idx != net_idx)
- goto done;
- l_debug("Finalize %s", fname);
- memcpy(key.old_key, key.new_key, 16);
- lseek(fd, 0, SEEK_SET);
- if (write(fd, &key, sizeof(key)) != sizeof(key))
- goto done;
- done:
- close(fd);
- }
- bool keyring_finalize_app_keys(struct mesh_node *node, uint16_t net_idx)
- {
- const char *node_path;
- char key_dir[PATH_MAX];
- DIR *dir;
- int dir_fd;
- struct dirent *entry;
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- if (strlen(node_path) + strlen(app_key_dir) + 1 >= PATH_MAX)
- return false;
- snprintf(key_dir, PATH_MAX, "%s%s", node_path, app_key_dir);
- dir = opendir(key_dir);
- if (!dir) {
- if (errno == ENOENT)
- return true;
- l_error("Failed to open AppKey storage directory: %s", key_dir);
- return false;
- }
- dir_fd = dirfd(dir);
- while ((entry = readdir(dir)) != NULL) {
- /* AppKeys are stored in regular files */
- if (entry->d_type == DT_REG)
- finalize(dir_fd, entry->d_name, net_idx);
- }
- closedir(dir);
- return true;
- }
- bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast,
- uint8_t count, uint8_t dev_key[16])
- {
- const char *node_path;
- char key_file[PATH_MAX];
- bool result = true;
- int fd, i;
- if (!IS_UNICAST_RANGE(unicast, count))
- return false;
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- if (strlen(node_path) + strlen(dev_key_dir) + 1 + 4 >= PATH_MAX)
- return false;
- snprintf(key_file, PATH_MAX, "%s%s", node_path, dev_key_dir);
- mkdir(key_file, 0755);
- for (i = 0; i < count; i++) {
- snprintf(key_file, PATH_MAX, "%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, 0600);
- if (fd >= 0) {
- if (write(fd, dev_key, 16) != 16)
- result = false;
- close(fd);
- } else
- result = false;
- }
- return result;
- }
- static bool get_key(struct mesh_node *node, const char *key_dir,
- uint16_t key_idx, void *key, ssize_t sz)
- {
- bool result = false;
- int fd;
- if (!key)
- return false;
- fd = open_key_file(node, key_dir, key_idx, O_RDONLY);
- if (fd >= 0) {
- if (read(fd, key, sz) == sz)
- result = true;
- close(fd);
- }
- return result;
- }
- bool keyring_get_net_key(struct mesh_node *node, uint16_t net_idx,
- struct keyring_net_key *key)
- {
- return get_key(node, net_key_dir, net_idx, key, sizeof(*key));
- }
- bool keyring_get_app_key(struct mesh_node *node, uint16_t app_idx,
- struct keyring_app_key *key)
- {
- return get_key(node, app_key_dir, app_idx, key, sizeof(*key));
- }
- bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast,
- uint8_t dev_key[16])
- {
- const char *node_path;
- char key_file[PATH_MAX];
- bool result = false;
- int fd;
- if (!IS_UNICAST(unicast))
- return false;
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- 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)
- result = true;
- close(fd);
- }
- return result;
- }
- bool keyring_del_net_key(struct mesh_node *node, uint16_t net_idx)
- {
- const char *node_path;
- char key_file[PATH_MAX];
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- snprintf(key_file, PATH_MAX, "%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() */
- return true;
- }
- bool keyring_del_app_key(struct mesh_node *node, uint16_t app_idx)
- {
- const char *node_path;
- char key_file[PATH_MAX];
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- snprintf(key_file, PATH_MAX, "%s%s/%3.3x", node_path, app_key_dir,
- app_idx);
- l_debug("RM App Key %s", key_file);
- remove(key_file);
- return true;
- }
- bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast,
- uint8_t count)
- {
- const char *node_path;
- char key_file[PATH_MAX];
- int i;
- if (!IS_UNICAST_RANGE(unicast, count))
- return false;
- if (!node)
- return false;
- node_path = node_get_storage_dir(node);
- for (i = 0; i < count; i++) {
- snprintf(key_file, PATH_MAX, "%s%s/%4.4x", node_path,
- dev_key_dir, unicast + i);
- l_debug("RM Dev Key %s", key_file);
- remove(key_file);
- }
- 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);
- }
|