mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 08:44:38 +08:00
c815b6ba30
This patch fixes a number of potential NULL pointer dereferencing cases.
601 lines
12 KiB
C
601 lines
12 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2019-2020 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "src/shared/shell.h"
|
|
#include "src/shared/util.h"
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
#include "tools/mesh/keys.h"
|
|
#include "tools/mesh/mesh-db.h"
|
|
#include "tools/mesh/remote.h"
|
|
#include "tools/mesh/util.h"
|
|
|
|
#define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
|
|
|
|
struct remote_key {
|
|
uint16_t idx;
|
|
bool updated;
|
|
};
|
|
|
|
struct remote_node {
|
|
uint16_t unicast;
|
|
struct l_queue *net_keys;
|
|
struct l_queue *app_keys;
|
|
struct l_queue **els;
|
|
bool comp;
|
|
uint8_t uuid[16];
|
|
uint8_t num_ele;
|
|
};
|
|
|
|
struct rejected_addr {
|
|
uint32_t iv_index;
|
|
uint16_t unicast;
|
|
};
|
|
|
|
static struct l_queue *nodes;
|
|
static struct l_queue *reject_list;
|
|
|
|
static int compare_mod_id(const void *a, const void *b, void *user_data)
|
|
{
|
|
uint32_t id1 = L_PTR_TO_UINT(a);
|
|
uint32_t id2 = L_PTR_TO_UINT(b);
|
|
|
|
if (id1 >= VENDOR_ID_MASK)
|
|
id1 &= ~VENDOR_ID_MASK;
|
|
|
|
if (id2 >= VENDOR_ID_MASK)
|
|
id2 &= ~VENDOR_ID_MASK;
|
|
|
|
if (id1 < id2)
|
|
return -1;
|
|
|
|
if (id1 > id2)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int compare_unicast(const void *a, const void *b, void *user_data)
|
|
{
|
|
const struct remote_node *a_rmt = a;
|
|
const struct remote_node *b_rmt = b;
|
|
|
|
if (a_rmt->unicast < b_rmt->unicast)
|
|
return -1;
|
|
|
|
if (a_rmt->unicast > b_rmt->unicast)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool match_node_addr(const void *a, const void *b)
|
|
{
|
|
const struct remote_node *rmt = a;
|
|
uint16_t addr = L_PTR_TO_UINT(b);
|
|
|
|
if (addr >= rmt->unicast &&
|
|
addr <= (rmt->unicast + rmt->num_ele - 1))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool match_key(const void *a, const void *b)
|
|
{
|
|
const struct remote_key *key = a;
|
|
uint16_t idx = L_PTR_TO_UINT(b);
|
|
|
|
return (key->idx == idx);
|
|
}
|
|
|
|
static bool match_bound_key(const void *a, const void *b)
|
|
{
|
|
const struct remote_key *app_key = a;
|
|
uint16_t net_idx = L_PTR_TO_UINT(b);
|
|
|
|
return (net_idx == keys_get_bound_key(app_key->idx));
|
|
}
|
|
|
|
uint8_t remote_del_node(uint16_t unicast)
|
|
{
|
|
struct remote_node *rmt;
|
|
uint8_t num_ele, i;
|
|
uint32_t iv_index = mesh_db_get_iv_index();
|
|
|
|
rmt = l_queue_remove_if(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
|
|
if (!rmt)
|
|
return 0;
|
|
|
|
num_ele = rmt->num_ele;
|
|
|
|
for (i = 0; i < num_ele; ++i) {
|
|
l_queue_destroy(rmt->els[i], NULL);
|
|
remote_add_rejected_address(unicast + i, iv_index, true);
|
|
}
|
|
|
|
l_free(rmt->els);
|
|
|
|
l_queue_destroy(rmt->net_keys, NULL);
|
|
l_queue_destroy(rmt->app_keys, NULL);
|
|
l_free(rmt);
|
|
|
|
mesh_db_del_node(unicast);
|
|
|
|
return num_ele;
|
|
}
|
|
|
|
bool remote_add_node(const uint8_t uuid[16], uint16_t unicast,
|
|
uint8_t ele_cnt, uint16_t net_idx)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
|
|
if (rmt)
|
|
return false;
|
|
|
|
rmt = l_new(struct remote_node, 1);
|
|
memcpy(rmt->uuid, uuid, 16);
|
|
rmt->unicast = unicast;
|
|
rmt->num_ele = ele_cnt;
|
|
rmt->net_keys = l_queue_new();
|
|
|
|
key = l_new(struct remote_key, 1);
|
|
key->idx = net_idx;
|
|
|
|
l_queue_push_tail(rmt->net_keys, key);
|
|
|
|
rmt->els = l_new(struct l_queue *, ele_cnt);
|
|
|
|
if (!nodes)
|
|
nodes = l_queue_new();
|
|
|
|
l_queue_insert(nodes, rmt, compare_unicast, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id,
|
|
bool vendor)
|
|
{
|
|
struct remote_node *rmt;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
if (ele_idx >= rmt->num_ele)
|
|
return false;
|
|
|
|
if (!rmt->els[ele_idx])
|
|
rmt->els[ele_idx] = l_queue_new();
|
|
|
|
if (!vendor)
|
|
mod_id = VENDOR_ID_MASK | mod_id;
|
|
|
|
l_queue_insert(rmt->els[ele_idx], L_UINT_TO_PTR(mod_id),
|
|
compare_mod_id, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
void remote_set_composition(uint16_t addr, bool comp)
|
|
{
|
|
struct remote_node *rmt;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return;
|
|
|
|
rmt->comp = comp;
|
|
}
|
|
|
|
bool remote_has_composition(uint16_t addr)
|
|
{
|
|
struct remote_node *rmt;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
return rmt->comp;
|
|
}
|
|
|
|
bool remote_add_net_key(uint16_t addr, uint16_t net_idx, bool save)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
if (l_queue_find(rmt->net_keys, match_key, L_UINT_TO_PTR(net_idx)))
|
|
return true;
|
|
|
|
key = l_new(struct remote_key, 1);
|
|
key->idx = net_idx;
|
|
|
|
l_queue_push_tail(rmt->net_keys, key);
|
|
|
|
if (save)
|
|
return mesh_db_node_add_net_key(addr, net_idx);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool remote_del_net_key(uint16_t addr, uint16_t net_idx)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
key = l_queue_remove_if(rmt->net_keys, match_key,
|
|
L_UINT_TO_PTR(net_idx));
|
|
if (!key)
|
|
return false;
|
|
|
|
mesh_db_node_del_net_key(addr, net_idx);
|
|
|
|
l_free(key);
|
|
key = l_queue_remove_if(rmt->app_keys, match_bound_key,
|
|
L_UINT_TO_PTR(net_idx));
|
|
|
|
while (key) {
|
|
mesh_db_node_del_app_key(rmt->unicast, key->idx);
|
|
l_free(key);
|
|
|
|
key = l_queue_remove_if(rmt->app_keys, match_bound_key,
|
|
L_UINT_TO_PTR(net_idx));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool remote_update_net_key(uint16_t addr, uint16_t net_idx, bool update,
|
|
bool save)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
key = l_queue_find(rmt->net_keys, match_key,
|
|
L_UINT_TO_PTR(net_idx));
|
|
if (!key)
|
|
return false;
|
|
|
|
key->updated = update;
|
|
|
|
if (save)
|
|
return mesh_db_node_update_net_key(addr, net_idx, update);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool remote_add_app_key(uint16_t addr, uint16_t app_idx, bool save)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
if (!rmt->app_keys)
|
|
rmt->app_keys = l_queue_new();
|
|
|
|
if (l_queue_find(rmt->app_keys, match_key, L_UINT_TO_PTR(app_idx)))
|
|
return true;
|
|
|
|
key = l_new(struct remote_key, 1);
|
|
key->idx = app_idx;
|
|
|
|
l_queue_push_tail(rmt->app_keys, key);
|
|
|
|
if (save)
|
|
return mesh_db_node_add_app_key(addr, app_idx);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool remote_del_app_key(uint16_t addr, uint16_t app_idx)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
key = l_queue_remove_if(rmt->app_keys, match_key,
|
|
L_UINT_TO_PTR(app_idx));
|
|
l_free(key);
|
|
|
|
return mesh_db_node_del_app_key(addr, app_idx);
|
|
}
|
|
|
|
bool remote_update_app_key(uint16_t addr, uint16_t app_idx, bool update,
|
|
bool save)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
key = l_queue_find(rmt->app_keys, match_key,
|
|
L_UINT_TO_PTR(app_idx));
|
|
if (!key)
|
|
return false;
|
|
|
|
key->updated = update;
|
|
|
|
if (save)
|
|
return mesh_db_node_update_app_key(addr, app_idx, update);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool remote_finish_key_refresh(uint16_t addr, uint16_t net_idx)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
const struct l_queue_entry *l;
|
|
bool res = true;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return false;
|
|
|
|
if (!remote_update_net_key(addr, net_idx, false, true))
|
|
return false;
|
|
|
|
l = l_queue_get_entries(rmt->app_keys);
|
|
|
|
for (; l; l = l->next) {
|
|
key = l->data;
|
|
|
|
if (net_idx != keys_get_bound_key(key->idx))
|
|
continue;
|
|
|
|
key->updated = false;
|
|
|
|
res &= mesh_db_node_update_app_key(addr, key->idx, false);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
uint16_t remote_get_subnet_idx(uint16_t addr)
|
|
{
|
|
struct remote_node *rmt;
|
|
struct remote_key *key;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
|
|
if (!rmt || l_queue_isempty(rmt->net_keys))
|
|
return NET_IDX_INVALID;
|
|
|
|
key = l_queue_peek_head(rmt->net_keys);
|
|
|
|
return key->idx;
|
|
}
|
|
|
|
static void print_key(void *data, void *user_data)
|
|
{
|
|
struct remote_key *key = data;
|
|
|
|
bt_shell_printf("%u (0x%3.3x) %s, ", key->idx, key->idx,
|
|
key->updated ? ", updated":"");
|
|
}
|
|
|
|
static void print_model(void *model, void *user_data)
|
|
{
|
|
uint32_t mod_id = L_PTR_TO_UINT(model);
|
|
|
|
if (mod_id >= VENDOR_ID_MASK) {
|
|
mod_id &= ~VENDOR_ID_MASK;
|
|
bt_shell_printf("\t\t\t" COLOR_GREEN "SIG model: %4.4x \"%s\"\n"
|
|
COLOR_OFF, mod_id, sig_model_string(mod_id));
|
|
return;
|
|
}
|
|
|
|
bt_shell_printf("\t\t\t" COLOR_GREEN "Vendor model: %8.8x\n"
|
|
COLOR_OFF, mod_id);
|
|
|
|
}
|
|
|
|
static void print_element(struct l_queue *mods, int idx)
|
|
{
|
|
if (!mods)
|
|
return;
|
|
|
|
bt_shell_printf("\t\t" COLOR_GREEN "element %u:\n" COLOR_OFF, idx);
|
|
l_queue_foreach(mods, print_model, NULL);
|
|
}
|
|
|
|
static void print_node(void *rmt, void *user_data)
|
|
{
|
|
struct remote_node *node = rmt;
|
|
int i;
|
|
char *str;
|
|
|
|
bt_shell_printf(COLOR_YELLOW "Mesh node:\n" COLOR_OFF);
|
|
str = l_util_hexstring_upper(node->uuid, 16);
|
|
bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str);
|
|
l_free(str);
|
|
bt_shell_printf("\t" COLOR_GREEN "primary = %4.4x\n" COLOR_OFF,
|
|
node->unicast);
|
|
bt_shell_printf("\t" COLOR_GREEN "net_keys = ");
|
|
l_queue_foreach(node->net_keys, print_key, NULL);
|
|
bt_shell_printf("\n" COLOR_OFF);
|
|
|
|
if (node->app_keys && !l_queue_isempty(node->app_keys)) {
|
|
bt_shell_printf("\t" COLOR_GREEN "app_keys = ");
|
|
l_queue_foreach(node->app_keys, print_key, NULL);
|
|
bt_shell_printf("\n" COLOR_OFF);
|
|
}
|
|
|
|
bt_shell_printf("\t" COLOR_GREEN "elements (%u):\n" COLOR_OFF,
|
|
node->num_ele);
|
|
|
|
for (i = 0; i < node->num_ele; ++i)
|
|
print_element(node->els[i], i);
|
|
}
|
|
|
|
static bool match_rejected_addr(const void *a, const void *b)
|
|
{
|
|
const struct rejected_addr *addr = a;
|
|
uint16_t unicast = L_PTR_TO_UINT(b);
|
|
|
|
return addr->unicast == unicast;
|
|
}
|
|
|
|
static uint16_t get_next_addr(uint16_t high, uint16_t addr,
|
|
uint8_t ele_cnt)
|
|
{
|
|
while ((addr + ele_cnt - 1) <= high) {
|
|
int i = 0;
|
|
|
|
for (i = 0; i < ele_cnt; i++) {
|
|
struct rejected_addr *reject;
|
|
|
|
reject = l_queue_find(reject_list, match_rejected_addr,
|
|
L_UINT_TO_PTR(addr + i));
|
|
if (!reject)
|
|
break;
|
|
}
|
|
|
|
addr += i;
|
|
|
|
if ((i != ele_cnt) && (addr + ele_cnt - 1) <= high)
|
|
return addr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool check_iv_index(const void *a, const void *b)
|
|
{
|
|
const struct rejected_addr *reject = a;
|
|
uint32_t iv_index = L_PTR_TO_UINT(b);
|
|
|
|
return (abs_diff(iv_index, reject->iv_index) > 2);
|
|
}
|
|
|
|
void remote_print_node(uint16_t addr)
|
|
{
|
|
struct remote_node *rmt;
|
|
|
|
if (!nodes)
|
|
return;
|
|
|
|
rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(addr));
|
|
if (!rmt)
|
|
return;
|
|
|
|
print_node(rmt, NULL);
|
|
}
|
|
|
|
void remote_print_all(void)
|
|
{
|
|
if (!nodes)
|
|
return;
|
|
|
|
l_queue_foreach(nodes, print_node, NULL);
|
|
}
|
|
|
|
uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt)
|
|
{
|
|
struct remote_node *rmt;
|
|
const struct l_queue_entry *l;
|
|
uint16_t addr;
|
|
|
|
/* Note: the address space includes both low and high terminal values */
|
|
if (ele_cnt > (high - low + 1))
|
|
return 0;
|
|
|
|
if (!nodes || l_queue_isempty(nodes))
|
|
return low;
|
|
|
|
addr = low;
|
|
l = l_queue_get_entries(nodes);
|
|
|
|
/* Cycle through the sorted (by unicast) node list */
|
|
for (; l; l = l->next) {
|
|
rmt = l->data;
|
|
|
|
if (rmt->unicast < low)
|
|
continue;
|
|
|
|
if (rmt->unicast >= (addr + ele_cnt)) {
|
|
uint16_t unicast;
|
|
|
|
unicast = get_next_addr(rmt->unicast - 1, addr,
|
|
ele_cnt);
|
|
if (unicast)
|
|
return unicast;
|
|
}
|
|
|
|
addr = rmt->unicast + rmt->num_ele;
|
|
}
|
|
|
|
addr = get_next_addr(high, addr, ele_cnt);
|
|
|
|
return addr;
|
|
}
|
|
|
|
void remote_add_rejected_address(uint16_t addr, uint32_t iv_index, bool save)
|
|
{
|
|
struct rejected_addr *reject;
|
|
|
|
if (!reject_list)
|
|
reject_list = l_queue_new();
|
|
|
|
reject = l_new(struct rejected_addr, 1);
|
|
reject->unicast = addr;
|
|
reject->iv_index = iv_index;
|
|
|
|
l_queue_push_tail(reject_list, reject);
|
|
|
|
if (save)
|
|
mesh_db_add_rejected_addr(addr, iv_index);
|
|
}
|
|
|
|
void remote_clear_rejected_addresses(uint32_t iv_index)
|
|
{
|
|
struct rejected_addr *reject;
|
|
|
|
reject = l_queue_remove_if(reject_list, check_iv_index,
|
|
L_UINT_TO_PTR(iv_index));
|
|
|
|
while (reject) {
|
|
l_free(reject);
|
|
reject = l_queue_remove_if(reject_list, check_iv_index,
|
|
L_UINT_TO_PTR(iv_index));
|
|
}
|
|
|
|
mesh_db_clear_rejected(iv_index);
|
|
}
|