mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 00:34:39 +08:00
a4f6905ea9
Fixed compiler flagged unsafe usage of strncpy
668 lines
14 KiB
C
668 lines
14 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2017-2018 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
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <json-c/json.h>
|
|
#include <ell/ell.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
|
|
#include "mesh/mesh.h"
|
|
#include "mesh/node.h"
|
|
|
|
#include "mesh/net.h"
|
|
#include "mesh/appkey.h"
|
|
#include "mesh/model.h"
|
|
#include "mesh/storage.h"
|
|
|
|
/*
|
|
* TODO: figure out naming convention to store alternative nodes
|
|
* Mesh storage dir wil be in configure.ac
|
|
*/
|
|
#define DEVICE_COMPOSITION_FILE "../config/composition.json"
|
|
#define NODE_CONGIGURATION_FILE "../config/configuration.json"
|
|
|
|
static bool read_local_node_cb(struct mesh_db_node *db_node, void *user_data)
|
|
{
|
|
struct mesh_net *net = user_data;
|
|
struct mesh_node *node;
|
|
uint32_t seq_number;
|
|
uint16_t crpl;
|
|
uint8_t ttl, mode, cnt, num_ele;
|
|
uint16_t unicast, interval;
|
|
uint8_t *uuid;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
node = node_create_from_storage(net, db_node, true);
|
|
if (!node)
|
|
return false;
|
|
|
|
mesh_net_local_node_set(net, node, db_node->provisioner);
|
|
seq_number = node_get_sequence_number(node);
|
|
mesh_net_set_seq_num(net, seq_number);
|
|
ttl = node_default_ttl_get(node);
|
|
mesh_net_set_default_ttl(net, ttl);
|
|
crpl = node_get_crpl(node);
|
|
mesh_net_set_crpl(net, crpl);
|
|
|
|
mode = node_proxy_mode_get(node);
|
|
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
|
|
mesh_net_set_proxy_mode(net, mode == MESH_MODE_ENABLED);
|
|
|
|
mode = node_friend_mode_get(node);
|
|
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
|
|
mesh_net_set_friend_mode(net, mode == MESH_MODE_ENABLED);
|
|
|
|
mode = node_relay_mode_get(node, &cnt, &interval);
|
|
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
|
|
mesh_net_set_relay_mode(net, mode == MESH_MODE_ENABLED, cnt,
|
|
interval);
|
|
|
|
mode = node_beacon_mode_get(node);
|
|
if (mode == MESH_MODE_ENABLED || mode == MESH_MODE_DISABLED)
|
|
mesh_net_set_beacon_mode(net, mode == MESH_MODE_ENABLED);
|
|
|
|
unicast = db_node->unicast;
|
|
num_ele = node_get_num_elements(node);
|
|
|
|
if (!IS_UNASSIGNED(unicast) &&
|
|
!mesh_net_register_unicast(net, unicast, num_ele))
|
|
return false;
|
|
|
|
uuid = node_uuid_get(node);
|
|
if (uuid)
|
|
mesh_net_id_uuid_set(net, uuid);
|
|
return true;
|
|
}
|
|
|
|
static bool read_net_keys_cb(uint16_t idx, uint8_t *key, uint8_t *new_key,
|
|
int phase, void *user_data)
|
|
{
|
|
struct mesh_net *net = user_data;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
if (mesh_net_add_key(net, false, idx, key) != MESH_STATUS_SUCCESS)
|
|
return false;
|
|
/* TODO: handle restoring key refresh phase and new keys */
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool read_app_keys_cb(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
|
|
uint8_t *new_key, void *user_data)
|
|
{
|
|
struct mesh_net *net = user_data;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
return appkey_key_init(net, net_idx, app_idx, key, new_key);
|
|
}
|
|
|
|
static bool parse_local_node(struct mesh_net *net, json_object *jnode)
|
|
{
|
|
bool bvalue;
|
|
uint32_t iv_index;
|
|
uint8_t key_buf[16];
|
|
uint8_t cnt;
|
|
uint16_t interval;
|
|
|
|
if (mesh_db_read_iv_index(jnode, &iv_index, &bvalue))
|
|
mesh_net_set_iv_index(net, iv_index, bvalue);
|
|
|
|
if (mesh_db_read_net_transmit(jnode, &cnt, &interval))
|
|
mesh_net_transmit_params_set(net, cnt, interval);
|
|
|
|
/* Node composition/configuration info */
|
|
if (!mesh_db_read_node(jnode, read_local_node_cb, net))
|
|
return false;
|
|
|
|
if (!mesh_db_read_net_keys(jnode, read_net_keys_cb, net))
|
|
return false;
|
|
|
|
/* TODO: use the actual "primary" network index for this node */
|
|
if (mesh_db_read_device_key(jnode, key_buf) &&
|
|
!node_set_device_key(mesh_net_local_node_get(net), key_buf))
|
|
return false;
|
|
|
|
mesh_db_read_app_keys(jnode, read_app_keys_cb, net);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool read_unprov_device_cb(struct mesh_db_node *db_node, void *user_data)
|
|
{
|
|
struct mesh_net *net = user_data;
|
|
struct mesh_node *node;
|
|
uint16_t crpl;
|
|
uint8_t *uuid;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
node = node_create_from_storage(net, db_node, true);
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
mesh_net_local_node_set(net, node, db_node->provisioner);
|
|
crpl = node_get_crpl(node);
|
|
mesh_net_set_crpl(net, crpl);
|
|
|
|
uuid = node_uuid_get(node);
|
|
if (uuid)
|
|
mesh_net_id_uuid_set(net, uuid);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool parse_unprovisioned_device(struct mesh_net *net, json_object *jnode)
|
|
{
|
|
struct mesh_db_prov prov;
|
|
struct mesh_net_prov_caps *caps;
|
|
struct mesh_node *node;
|
|
|
|
/* Node composition/configuration info */
|
|
if (!mesh_db_read_unprovisioned_device(jnode,
|
|
read_unprov_device_cb, net))
|
|
return false;
|
|
|
|
if (!mesh_db_read_prov_info(jnode, &prov))
|
|
return false;
|
|
|
|
caps = mesh_net_prov_caps_get(net);
|
|
if (!caps)
|
|
return false;
|
|
|
|
node = mesh_net_local_node_get(net);
|
|
if (!node)
|
|
return false;
|
|
|
|
caps->num_ele = node_get_num_elements(node);
|
|
l_put_le16(prov.algorithm, &caps->algorithms);
|
|
caps->pub_type = prov.pub_type;
|
|
caps->static_type = prov.static_type;
|
|
caps->output_size = prov.output_oob.size;
|
|
l_put_le16(prov.output_oob.actions, &caps->output_action);
|
|
caps->input_size = prov.input_oob.size;
|
|
l_put_le16(prov.input_oob.actions, &caps->input_action);
|
|
|
|
return mesh_net_priv_key_set(net, prov.priv_key);
|
|
}
|
|
|
|
static bool parse_config(struct mesh_net *net, const char *config_name,
|
|
bool unprovisioned)
|
|
{
|
|
int fd;
|
|
char *str;
|
|
const char *out;
|
|
struct stat st;
|
|
ssize_t sz;
|
|
json_object *jnode = NULL;
|
|
bool result = false;
|
|
|
|
if (!config_name)
|
|
return false;
|
|
|
|
fd = open(config_name, O_RDONLY);
|
|
if (!fd)
|
|
return false;
|
|
|
|
if (fstat(fd, &st) == -1) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
str = (char *) l_new(char, st.st_size + 1);
|
|
if (!str) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
sz = read(fd, str, st.st_size);
|
|
if (sz != st.st_size) {
|
|
l_error("Failed to read configuration file");
|
|
goto done;
|
|
}
|
|
|
|
jnode = json_tokener_parse(str);
|
|
if (!jnode)
|
|
goto done;
|
|
|
|
mesh_net_jconfig_set(net, jnode);
|
|
|
|
if (!unprovisioned)
|
|
result = parse_local_node(net, jnode);
|
|
else
|
|
result = parse_unprovisioned_device(net, jnode);
|
|
|
|
if (!result) {
|
|
storage_release(net);
|
|
goto done;
|
|
}
|
|
|
|
mesh_net_cfg_file_get(net, &out);
|
|
if (!out)
|
|
mesh_net_cfg_file_set(net, !unprovisioned ?
|
|
config_name : NODE_CONGIGURATION_FILE);
|
|
done:
|
|
close(fd);
|
|
if (str)
|
|
l_free(str);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool storage_parse_config(struct mesh_net *net, const char *config_name)
|
|
{
|
|
bool result = false;
|
|
bool unprovisioned = !config_name;
|
|
|
|
if (unprovisioned) {
|
|
result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
|
|
goto done;
|
|
}
|
|
|
|
result = parse_config(net, config_name, false);
|
|
|
|
if (!result) {
|
|
size_t len = strlen(config_name) + 5;
|
|
char *bak = l_malloc(len);
|
|
|
|
/* Fall-back to Backup version */
|
|
strncpy(bak, config_name, len);
|
|
bak = strncat(bak, ".bak", 5);
|
|
|
|
remove(config_name);
|
|
rename(bak, config_name);
|
|
|
|
result = parse_config(net, config_name, false);
|
|
|
|
l_free(bak);
|
|
}
|
|
|
|
/* If configuration read fails, try as unprovisioned device */
|
|
if (!result) {
|
|
l_info("Parse configuration failed, trying unprovisioned");
|
|
unprovisioned = true;
|
|
result = parse_config(net, DEVICE_COMPOSITION_FILE, true);
|
|
}
|
|
|
|
done:
|
|
if (result)
|
|
mesh_net_provisioned_set(net, !unprovisioned);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool storage_local_set_ttl(struct mesh_net *net, uint8_t ttl)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_int(jnode, "defaultTTL", ttl);
|
|
}
|
|
|
|
bool storage_local_set_relay(struct mesh_net *net, bool enable,
|
|
uint8_t count, uint8_t interval)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_relay_mode(jnode, enable, count, interval);
|
|
}
|
|
|
|
bool storage_local_set_transmit_params(struct mesh_net *net, uint8_t count,
|
|
uint8_t interval)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_net_transmit(jnode, count, interval);
|
|
}
|
|
|
|
bool storage_local_set_mode(struct mesh_net *net, uint8_t mode,
|
|
const char *mode_name)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net || !mode_name)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_mode(jnode, mode_name, mode);
|
|
}
|
|
|
|
bool storage_model_bind(struct mesh_net *net, uint16_t addr, uint32_t mod_id,
|
|
uint16_t app_idx, bool unbind)
|
|
{
|
|
json_object *jnode;
|
|
bool is_local;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
is_local = mesh_net_is_local_address(net, addr);
|
|
if (is_local) {
|
|
int ele_idx;
|
|
bool is_vendor = (mod_id > 0xffff);
|
|
|
|
ele_idx = node_get_element_idx(mesh_net_local_node_get(net),
|
|
addr);
|
|
if (ele_idx < 0)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (unbind)
|
|
return mesh_db_model_binding_del(jnode, ele_idx,
|
|
is_vendor, mod_id, app_idx);
|
|
else
|
|
return mesh_db_model_binding_add(jnode, ele_idx,
|
|
is_vendor, mod_id, app_idx);
|
|
}
|
|
|
|
/* TODO: write remote node bindings to provisioner DB */
|
|
return false;
|
|
}
|
|
|
|
bool storage_local_app_key_add(struct mesh_net *net, uint16_t net_idx,
|
|
uint16_t app_idx, const uint8_t key[16], bool update)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_app_key_add(jnode, net_idx, app_idx, key, update);
|
|
}
|
|
|
|
bool storage_local_app_key_del(struct mesh_net *net, uint16_t net_idx,
|
|
uint16_t app_idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_app_key_del(jnode, net_idx, app_idx);
|
|
|
|
}
|
|
|
|
bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
|
|
const uint8_t key[16], int phase)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_net_key_add(jnode, net_idx, key, phase);
|
|
}
|
|
|
|
bool storage_local_net_key_del(struct mesh_net *net, uint16_t net_idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_net_key_del(jnode, net_idx);
|
|
}
|
|
|
|
bool storage_local_set_iv_index(struct mesh_net *net, uint32_t iv_index,
|
|
bool update)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_iv_index(jnode, iv_index, update);
|
|
}
|
|
|
|
bool storage_local_set_device_key(struct mesh_net *net, uint8_t dev_key[16])
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_device_key(jnode, dev_key);
|
|
}
|
|
|
|
bool storage_local_set_unicast(struct mesh_net *net, uint16_t unicast)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return mesh_db_write_uint16_hex(jnode, "unicastAddress", unicast);
|
|
}
|
|
|
|
bool storage_local_write_sequence_number(struct mesh_net *net, uint32_t seq)
|
|
{
|
|
json_object *jnode;
|
|
const char *cfg_file;
|
|
bool result;
|
|
|
|
if (!net)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
result = mesh_db_write_int(jnode, "sequenceNumber", seq);
|
|
if (!result)
|
|
return false;
|
|
|
|
result = mesh_net_cfg_file_get(net, &cfg_file);
|
|
if (result && cfg_file)
|
|
result = storage_save_config(net, cfg_file, false, NULL, NULL);
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool save_config(struct mesh_net *net, const char *config_name)
|
|
{
|
|
FILE *outfile;
|
|
const char *str;
|
|
json_object *jnode;
|
|
bool result = false;
|
|
|
|
if (!net || !config_name)
|
|
return false;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
outfile = fopen(config_name, "w");
|
|
if (!outfile) {
|
|
l_error("Failed to save configuration to %s", config_name);
|
|
return false;
|
|
}
|
|
|
|
str = json_object_to_json_string_ext(jnode, JSON_C_TO_STRING_PRETTY);
|
|
|
|
if (fwrite(str, sizeof(char), strlen(str), outfile) < strlen(str))
|
|
l_warn("Incomplete write of mesh configuration");
|
|
else
|
|
result = true;
|
|
|
|
fclose(outfile);
|
|
|
|
return result;
|
|
}
|
|
|
|
struct write_info {
|
|
const char *config_name;
|
|
struct mesh_net *net;
|
|
void *user_data;
|
|
mesh_status_func_t cb;
|
|
};
|
|
|
|
static void idle_save_config(void *user_data)
|
|
{
|
|
struct write_info *info = user_data;
|
|
size_t len = strlen(info->config_name) + 5;
|
|
char *tmp = l_malloc(len);
|
|
char *bak = l_malloc(len);
|
|
bool result = false;
|
|
|
|
strncpy(tmp, info->config_name, len);
|
|
strncpy(bak, info->config_name, len);
|
|
tmp = strncat(tmp, ".tmp", 5);
|
|
bak = strncat(bak, ".bak", 5);
|
|
remove(tmp);
|
|
|
|
l_debug("Storage-Wrote");
|
|
result = save_config(info->net, tmp);
|
|
|
|
if (result) {
|
|
remove(bak);
|
|
rename(info->config_name, bak);
|
|
rename(tmp, info->config_name);
|
|
}
|
|
|
|
remove(tmp);
|
|
l_free(tmp);
|
|
l_free(bak);
|
|
|
|
if (info->cb)
|
|
info->cb(info->user_data, result);
|
|
|
|
l_free(info);
|
|
}
|
|
|
|
bool storage_save_config(struct mesh_net *net, const char *config_name,
|
|
bool no_wait, mesh_status_func_t cb, void *user_data)
|
|
{
|
|
struct write_info *info;
|
|
|
|
info = l_new(struct write_info, 1);
|
|
if (!info)
|
|
return false;
|
|
|
|
info->net = net;
|
|
info->config_name = config_name;
|
|
info->cb = cb;
|
|
info->user_data = user_data;
|
|
|
|
if (no_wait)
|
|
idle_save_config(info);
|
|
l_idle_oneshot(idle_save_config, info, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool storage_save_new_config(struct mesh_net *net, const char *config_name,
|
|
mesh_status_func_t cb, void *user_data)
|
|
{
|
|
json_object *jnode;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
mesh_db_remove_property(jnode, "provision");
|
|
|
|
return storage_save_config(net, config_name, false, cb, user_data);
|
|
}
|
|
|
|
void storage_release(struct mesh_net *net)
|
|
{
|
|
json_object *jnode;
|
|
|
|
jnode = mesh_net_jconfig_get(net);
|
|
if (jnode)
|
|
json_object_put(jnode);
|
|
|
|
mesh_net_jconfig_set(net, NULL);
|
|
}
|