zephyr/subsys/bluetooth/mesh/app_keys.c
Pavel Vasilyev 6dfc9ecc03 Bluetooth: Mesh: Invalidate pending entries before calling settings API
If, while storing app or net key, or changing cdb, we receive another
mesh message that affects setting of the same key that the mesh
currently stores, the new change won't be stored.

The settings subsystem API has rescheduling point inside. The mesh
settings are stored in the system workqueue and by default mesh messages
are processed from BT RX thread which has higher priority than the
system workqueue.

When the case above happens, a new change will be written to the same
cache entry. But this cache entry will be invalidated after leaving
settings API, which in turns will invalidate the new change:

- Receive Config AppKey Add message
cfg_srv.c -> app_keys.c: bt_mesh_app_key_add()
app_keys.c: update_app_key_settings()
app_keys.c: bt_mesh_settings_store_schedule()
- store_pending() in settings.c is scheduled
settings.c -> app_keys.c: bt_mesh_app_key_pending_store()
app_keys.c: store_app_key() called to store new app key
app_keys.c: settings_save_one() calls flash driver and sleeps
- Receive Config AppKey Delete message while in sleep,
  which wakes up BT RX thread before returning to the system workqueue
cfg_srv.c -> app_keys.c: bt_mesh_app_key_del()
app_keys.c: update_app_key_settings()
app_keys.c: app_key_update_find() finds entry and returns it
app_keys.c: update->clear = 1
app_keys.c: bt_mesh_settings_store_schedule()
- returning back to bt_mesh_app_key_pending_store() from
  settings_save_one()
app_keys.c: update->valid = 0
- the key won't be deleted next time store_pending() is scheduled.

This change moves entry invalidation before calling settings API so that
after returning from settings API, a new change won't be unintentionally
invalidated.

Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2023-01-18 10:47:44 +01:00

676 lines
14 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/conn.h>
#include "mesh.h"
#include "net.h"
#include "app_keys.h"
#include "rpl.h"
#include "settings.h"
#include "crypto.h"
#include "adv.h"
#include "proxy.h"
#include "friend.h"
#include "foundation.h"
#include "access.h"
#include "common/bt_str.h"
#define LOG_LEVEL CONFIG_BT_MESH_KEYS_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_app_keys);
/* Tracking of what storage changes are pending for App Keys. We track this in
* a separate array here instead of within the respective bt_mesh_app_key
* struct itself, since once a key gets deleted its struct becomes invalid
* and may be reused for other keys.
*/
struct app_key_update {
uint16_t key_idx:12, /* AppKey Index */
valid:1, /* 1 if this entry is valid, 0 if not */
clear:1; /* 1 if key needs clearing, 0 if storing */
};
/* AppKey information for persistent storage. */
struct app_key_val {
uint16_t net_idx;
bool updated;
uint8_t val[2][16];
} __packed;
/** Mesh Application Key. */
struct app_key {
uint16_t net_idx;
uint16_t app_idx;
bool updated;
struct bt_mesh_app_cred {
uint8_t id;
uint8_t val[16];
} keys[2];
};
static struct app_key_update app_key_updates[CONFIG_BT_MESH_APP_KEY_COUNT];
static struct app_key apps[CONFIG_BT_MESH_APP_KEY_COUNT] = {
[0 ... (CONFIG_BT_MESH_APP_KEY_COUNT - 1)] = {
.app_idx = BT_MESH_KEY_UNUSED,
.net_idx = BT_MESH_KEY_UNUSED,
}
};
static struct app_key *app_get(uint16_t app_idx)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
if (apps[i].app_idx == app_idx) {
return &apps[i];
}
}
return NULL;
}
static void clear_app_key(uint16_t app_idx)
{
char path[20];
int err;
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
err = settings_delete(path);
if (err) {
LOG_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
} else {
LOG_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
}
}
static void store_app_key(uint16_t app_idx)
{
const struct app_key *app;
struct app_key_val key;
char path[20];
int err;
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
app = app_get(app_idx);
if (!app) {
LOG_WRN("ApKeyIndex 0x%03x not found", app_idx);
return;
}
key.net_idx = app->net_idx,
key.updated = app->updated,
memcpy(key.val[0], app->keys[0].val, 16);
memcpy(key.val[1], app->keys[1].val, 16);
err = settings_save_one(path, &key, sizeof(key));
if (err) {
LOG_ERR("Failed to store AppKey %s value", path);
} else {
LOG_DBG("Stored AppKey %s value", path);
}
}
static struct app_key_update *app_key_update_find(uint16_t key_idx,
struct app_key_update **free_slot)
{
struct app_key_update *match;
int i;
match = NULL;
*free_slot = NULL;
for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
struct app_key_update *update = &app_key_updates[i];
if (!update->valid) {
*free_slot = update;
continue;
}
if (update->key_idx == key_idx) {
match = update;
}
}
return match;
}
static void update_app_key_settings(uint16_t app_idx, bool store)
{
struct app_key_update *update, *free_slot;
uint8_t clear = store ? 0U : 1U;
LOG_DBG("AppKeyIndex 0x%03x", app_idx);
update = app_key_update_find(app_idx, &free_slot);
if (update) {
update->clear = clear;
bt_mesh_settings_store_schedule(
BT_MESH_SETTINGS_APP_KEYS_PENDING);
return;
}
if (!free_slot) {
if (store) {
store_app_key(app_idx);
} else {
clear_app_key(app_idx);
}
return;
}
free_slot->valid = 1U;
free_slot->key_idx = app_idx;
free_slot->clear = clear;
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_APP_KEYS_PENDING);
}
static void app_key_evt(struct app_key *app, enum bt_mesh_key_evt evt)
{
STRUCT_SECTION_FOREACH(bt_mesh_app_key_cb, cb) {
cb->evt_handler(app->app_idx, app->net_idx, evt);
}
}
static struct app_key *app_key_alloc(uint16_t app_idx)
{
struct app_key *app = NULL;
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
/* Check for already existing app_key */
if (apps[i].app_idx == app_idx) {
return &apps[i];
}
if (!app && apps[i].app_idx == BT_MESH_KEY_UNUSED) {
app = &apps[i];
}
}
return app;
}
static void app_key_del(struct app_key *app)
{
LOG_DBG("AppIdx 0x%03x", app->app_idx);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
update_app_key_settings(app->app_idx, false);
}
app_key_evt(app, BT_MESH_KEY_DELETED);
app->net_idx = BT_MESH_KEY_UNUSED;
app->app_idx = BT_MESH_KEY_UNUSED;
(void)memset(app->keys, 0, sizeof(app->keys));
}
static void app_key_revoke(struct app_key *app)
{
if (!app->updated) {
return;
}
memcpy(&app->keys[0], &app->keys[1], sizeof(app->keys[0]));
memset(&app->keys[1], 0, sizeof(app->keys[1]));
app->updated = false;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_REVOKED);
}
uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16])
{
struct app_key *app;
LOG_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx, bt_hex(key, 16));
if (!bt_mesh_subnet_get(net_idx)) {
return STATUS_INVALID_NETKEY;
}
app = app_key_alloc(app_idx);
if (!app) {
return STATUS_INSUFF_RESOURCES;
}
if (app->app_idx == app_idx) {
if (app->net_idx != net_idx) {
return STATUS_INVALID_NETKEY;
}
if (memcmp(key, app->keys[0].val, 16)) {
return STATUS_IDX_ALREADY_STORED;
}
return STATUS_SUCCESS;
}
if (bt_mesh_app_id(key, &app->keys[0].id)) {
return STATUS_CANNOT_SET;
}
LOG_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
app->net_idx = net_idx;
app->app_idx = app_idx;
app->updated = false;
memcpy(app->keys[0].val, key, 16);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_DBG("Storing AppKey persistently");
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_ADDED);
return STATUS_SUCCESS;
}
uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16])
{
struct app_key *app;
struct bt_mesh_subnet *sub;
LOG_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx, bt_hex(key, 16));
app = app_get(app_idx);
if (!app) {
return STATUS_INVALID_APPKEY;
}
if (net_idx != BT_MESH_KEY_UNUSED && app->net_idx != net_idx) {
return STATUS_INVALID_BINDING;
}
sub = bt_mesh_subnet_get(app->net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
/* The AppKey Update message shall generate an error when node
* is in normal operation, Phase 2, or Phase 3 or in Phase 1
* when the AppKey Update message on a valid AppKeyIndex when
* the AppKey value is different.
*/
if (sub->kr_phase != BT_MESH_KR_PHASE_1) {
return STATUS_CANNOT_UPDATE;
}
if (app->updated) {
if (memcmp(app->keys[1].val, key, 16)) {
return STATUS_IDX_ALREADY_STORED;
}
return STATUS_SUCCESS;
}
if (bt_mesh_app_id(key, &app->keys[1].id)) {
return STATUS_CANNOT_UPDATE;
}
LOG_DBG("app_idx 0x%04x AID 0x%02x", app_idx, app->keys[1].id);
app->updated = true;
memcpy(app->keys[1].val, key, 16);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_DBG("Storing AppKey persistently");
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_UPDATED);
return STATUS_SUCCESS;
}
uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx)
{
struct app_key *app;
LOG_DBG("AppIdx 0x%03x", app_idx);
if (net_idx != BT_MESH_KEY_UNUSED && !bt_mesh_subnet_get(net_idx)) {
return STATUS_INVALID_NETKEY;
}
app = app_get(app_idx);
if (!app) {
/* This could be a retry of a previous attempt that had its
* response lost, so pretend that it was a success.
*/
return STATUS_SUCCESS;
}
if (net_idx != BT_MESH_KEY_UNUSED && net_idx != app->net_idx) {
return STATUS_INVALID_BINDING;
}
app_key_del(app);
return STATUS_SUCCESS;
}
int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
const uint8_t old_key[16], const uint8_t new_key[16])
{
struct app_key *app;
app = app_key_alloc(app_idx);
if (!app) {
return -ENOMEM;
}
if (app->app_idx == app_idx) {
return 0;
}
LOG_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
memcpy(app->keys[0].val, old_key, 16);
if (bt_mesh_app_id(old_key, &app->keys[0].id)) {
return -EIO;
}
if (new_key) {
memcpy(app->keys[1].val, new_key, 16);
if (bt_mesh_app_id(new_key, &app->keys[1].id)) {
return -EIO;
}
}
app->net_idx = net_idx;
app->app_idx = app_idx;
app->updated = !!new_key;
return 0;
}
bool bt_mesh_app_key_exists(uint16_t app_idx)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
if (apps[i].app_idx == app_idx) {
return true;
}
}
return false;
}
ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
off_t skip)
{
size_t count = 0;
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (net_idx != BT_MESH_KEY_ANY && app->net_idx != net_idx) {
continue;
}
if (skip) {
skip--;
continue;
}
if (count >= max) {
return -ENOMEM;
}
app_idxs[count++] = app->app_idx;
}
return count;
}
int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
struct bt_mesh_subnet **sub,
const uint8_t **app_key, uint8_t *aid)
{
struct app_key *app = NULL;
if (BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
/* With device keys, the application has to decide which subnet
* to send on.
*/
*sub = bt_mesh_subnet_get(ctx->net_idx);
if (!*sub) {
LOG_WRN("Unknown NetKey 0x%03x", ctx->net_idx);
return -EINVAL;
}
if (ctx->app_idx == BT_MESH_KEY_DEV_REMOTE &&
!bt_mesh_has_addr(ctx->addr)) {
struct bt_mesh_cdb_node *node;
if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
LOG_WRN("No DevKey for 0x%04x", ctx->addr);
return -EINVAL;
}
node = bt_mesh_cdb_node_get(ctx->addr);
if (!node) {
LOG_WRN("No DevKey for 0x%04x", ctx->addr);
return -EINVAL;
}
*app_key = node->dev_key;
} else {
*app_key = bt_mesh.dev_key;
}
*aid = 0;
return 0;
}
app = app_get(ctx->app_idx);
if (!app) {
LOG_WRN("Unknown AppKey 0x%03x", ctx->app_idx);
return -EINVAL;
}
*sub = bt_mesh_subnet_get(app->net_idx);
if (!*sub) {
LOG_WRN("Unknown NetKey 0x%03x", app->net_idx);
return -EINVAL;
}
if ((*sub)->kr_phase == BT_MESH_KR_PHASE_2 && app->updated) {
*aid = app->keys[1].id;
*app_key = app->keys[1].val;
} else {
*aid = app->keys[0].id;
*app_key = app->keys[0].val;
}
return 0;
}
uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
struct bt_mesh_net_rx *rx,
int (*cb)(struct bt_mesh_net_rx *rx,
const uint8_t key[16], void *cb_data),
void *cb_data)
{
int err, i;
if (dev_key) {
/* Attempt remote dev key first, as that is only available for
* provisioner devices, which normally don't interact with nodes
* that know their local dev key.
*/
if (IS_ENABLED(CONFIG_BT_MESH_CDB) &&
rx->net_if != BT_MESH_NET_IF_LOCAL) {
struct bt_mesh_cdb_node *node;
node = bt_mesh_cdb_node_get(rx->ctx.addr);
if (node && !cb(rx, node->dev_key, cb_data)) {
return BT_MESH_KEY_DEV_REMOTE;
}
}
/** Bluetooth Mesh Specification v1.0.1, section 3.4.3:
* The Device key is only valid for unicast addresses.
*/
if (BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) {
err = cb(rx, bt_mesh.dev_key, cb_data);
if (!err) {
return BT_MESH_KEY_DEV_LOCAL;
}
}
return BT_MESH_KEY_UNUSED;
}
for (i = 0; i < ARRAY_SIZE(apps); i++) {
const struct app_key *app = &apps[i];
const struct bt_mesh_app_cred *cred;
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (app->net_idx != rx->sub->net_idx) {
continue;
}
if (rx->new_key && app->updated) {
cred = &app->keys[1];
} else {
cred = &app->keys[0];
}
if (cred->id != aid) {
continue;
}
err = cb(rx, cred->val, cb_data);
if (err) {
continue;
}
return app->app_idx;
}
return BT_MESH_KEY_UNUSED;
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_UPDATED || evt == BT_MESH_KEY_ADDED) {
return;
}
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (app->net_idx != sub->net_idx) {
continue;
}
if (evt == BT_MESH_KEY_DELETED) {
app_key_del(app);
} else if (evt == BT_MESH_KEY_REVOKED) {
app_key_revoke(app);
} else if (evt == BT_MESH_KEY_SWAPPED && app->updated) {
app_key_evt(app, BT_MESH_KEY_SWAPPED);
}
}
}
BT_MESH_SUBNET_CB_DEFINE(app_keys) = {
.evt_handler = subnet_evt,
};
void bt_mesh_app_keys_reset(void)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx != BT_MESH_KEY_UNUSED) {
app_key_del(app);
}
}
}
static int app_key_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct app_key_val key;
uint16_t app_idx;
int err;
if (!name) {
LOG_ERR("Insufficient number of arguments");
return -ENOENT;
}
app_idx = strtol(name, NULL, 16);
if (!len_rd) {
return 0;
}
err = bt_mesh_settings_set(read_cb, cb_arg, &key, sizeof(key));
if (err < 0) {
return -EINVAL;
}
err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0],
key.updated ? key.val[1] : NULL);
if (err) {
LOG_ERR("Failed to set \'app-key\'");
return err;
}
LOG_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
return 0;
}
BT_MESH_SETTINGS_DEFINE(app, "AppKey", app_key_set);
void bt_mesh_app_key_pending_store(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
struct app_key_update *update = &app_key_updates[i];
if (!update->valid) {
continue;
}
update->valid = 0U;
if (update->clear) {
clear_app_key(update->key_idx);
} else {
store_app_key(update->key_idx);
}
}
}