mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 00:34:39 +08:00
eb642a59fb
Add implementation for new method ExportKeys() on org.bluez.mesh.Management1 interface. This method is used by the authorized application to export information about network keys, application keys and device keys present in the local key database.
867 lines
24 KiB
C
867 lines
24 KiB
C
// 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 <ell/ell.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
#include "mesh/dbus.h"
|
|
#include "mesh/error.h"
|
|
#include "mesh/mesh.h"
|
|
#include "mesh/mesh-io.h"
|
|
#include "mesh/node.h"
|
|
#include "mesh/net.h"
|
|
#include "mesh/keyring.h"
|
|
#include "mesh/agent.h"
|
|
#include "mesh/provision.h"
|
|
#include "mesh/manager.h"
|
|
|
|
struct add_data{
|
|
struct l_dbus_message *msg;
|
|
struct mesh_agent *agent;
|
|
struct mesh_node *node;
|
|
uint32_t disc_watch;
|
|
uint16_t primary;
|
|
uint16_t net_idx;
|
|
uint8_t num_ele;
|
|
uint8_t uuid[16];
|
|
};
|
|
|
|
static int8_t scan_rssi;
|
|
static uint8_t scan_uuid[16];
|
|
static struct mesh_node *scan_node;
|
|
static struct l_timeout *scan_timeout;
|
|
static struct add_data *add_pending;
|
|
static const uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00};
|
|
|
|
static void scan_cancel(struct l_timeout *timeout, void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct mesh_io *io;
|
|
struct mesh_net *net;
|
|
|
|
l_debug("");
|
|
|
|
if (scan_timeout)
|
|
l_timeout_remove(scan_timeout);
|
|
|
|
net = node_get_net(node);
|
|
io = mesh_net_get_io(net);
|
|
mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb));
|
|
scan_node = NULL;
|
|
scan_timeout = NULL;
|
|
}
|
|
|
|
static void free_pending_add_call()
|
|
{
|
|
if (!add_pending)
|
|
return;
|
|
|
|
if (add_pending->disc_watch)
|
|
l_dbus_remove_watch(dbus_get_bus(),
|
|
add_pending->disc_watch);
|
|
|
|
if (add_pending->msg)
|
|
l_dbus_message_unref(add_pending->msg);
|
|
|
|
l_free(add_pending);
|
|
add_pending = NULL;
|
|
}
|
|
|
|
static void prov_disc_cb(struct l_dbus *bus, void *user_data)
|
|
{
|
|
if (!add_pending)
|
|
return;
|
|
|
|
initiator_cancel(add_pending);
|
|
add_pending->disc_watch = 0;
|
|
|
|
free_pending_add_call();
|
|
}
|
|
|
|
static void send_add_failed(const char *owner, const char *path,
|
|
uint8_t status)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message_builder *builder;
|
|
struct l_dbus_message *msg;
|
|
|
|
msg = l_dbus_message_new_method_call(dbus, owner, path,
|
|
MESH_PROVISIONER_INTERFACE,
|
|
"AddNodeFailed");
|
|
|
|
builder = l_dbus_message_builder_new(msg);
|
|
dbus_append_byte_array(builder, add_pending->uuid, 16);
|
|
l_dbus_message_builder_append_basic(builder, 's',
|
|
mesh_prov_status_str(status));
|
|
l_dbus_message_builder_finalize(builder);
|
|
l_dbus_message_builder_destroy(builder);
|
|
l_dbus_send(dbus, msg);
|
|
|
|
free_pending_add_call();
|
|
}
|
|
|
|
static bool add_cmplt(void *user_data, uint8_t status,
|
|
struct mesh_prov_node_info *info)
|
|
{
|
|
struct add_data *pending = user_data;
|
|
struct mesh_node *node = pending->node;
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message_builder *builder;
|
|
struct l_dbus_message *msg;
|
|
bool result;
|
|
|
|
if (pending != add_pending)
|
|
return false;
|
|
|
|
if (status != PROV_ERR_SUCCESS) {
|
|
send_add_failed(node_get_owner(node), node_get_app_path(node),
|
|
status);
|
|
return false;
|
|
}
|
|
|
|
result = keyring_put_remote_dev_key(add_pending->node, info->unicast,
|
|
info->num_ele, info->device_key);
|
|
|
|
if (!result) {
|
|
send_add_failed(node_get_owner(node), node_get_app_path(node),
|
|
PROV_ERR_CANT_ASSIGN_ADDR);
|
|
return false;
|
|
}
|
|
|
|
msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
|
|
node_get_app_path(node),
|
|
MESH_PROVISIONER_INTERFACE,
|
|
"AddNodeComplete");
|
|
|
|
builder = l_dbus_message_builder_new(msg);
|
|
dbus_append_byte_array(builder, add_pending->uuid, 16);
|
|
l_dbus_message_builder_append_basic(builder, 'q', &info->unicast);
|
|
l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele);
|
|
l_dbus_message_builder_finalize(builder);
|
|
l_dbus_message_builder_destroy(builder);
|
|
|
|
l_dbus_send(dbus, msg);
|
|
|
|
free_pending_add_call();
|
|
|
|
return true;
|
|
}
|
|
|
|
static void mgr_prov_data (struct l_dbus_message *reply, void *user_data)
|
|
{
|
|
struct add_data *pending = user_data;
|
|
uint16_t net_idx;
|
|
uint16_t primary;
|
|
|
|
if (pending != add_pending)
|
|
return;
|
|
|
|
if (l_dbus_message_is_error(reply))
|
|
return;
|
|
|
|
if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary))
|
|
return;
|
|
|
|
add_pending->primary = primary;
|
|
add_pending->net_idx = net_idx;
|
|
initiator_prov_data(net_idx, primary, add_pending);
|
|
}
|
|
|
|
static bool add_data_get(void *user_data, uint8_t num_ele)
|
|
{
|
|
struct add_data *pending = user_data;
|
|
struct l_dbus_message *msg;
|
|
struct l_dbus *dbus;
|
|
const char *app_path;
|
|
const char *sender;
|
|
|
|
if (pending != add_pending)
|
|
return false;
|
|
|
|
dbus = dbus_get_bus();
|
|
app_path = node_get_app_path(add_pending->node);
|
|
sender = node_get_owner(add_pending->node);
|
|
|
|
msg = l_dbus_message_new_method_call(dbus, sender, app_path,
|
|
MESH_PROVISIONER_INTERFACE,
|
|
"RequestProvData");
|
|
|
|
l_dbus_message_set_arguments(msg, "y", num_ele);
|
|
l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL);
|
|
|
|
add_pending->num_ele = num_ele;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void add_start(void *user_data, int err)
|
|
{
|
|
struct l_dbus_message *reply;
|
|
|
|
l_debug("Start callback");
|
|
|
|
if (err == MESH_ERROR_NONE)
|
|
reply = l_dbus_message_new_method_return(add_pending->msg);
|
|
else
|
|
reply = dbus_error(add_pending->msg, MESH_ERROR_FAILED,
|
|
"Failed to start provisioning initiator");
|
|
|
|
l_dbus_send(dbus_get_bus(), reply);
|
|
l_dbus_message_unref(add_pending->msg);
|
|
|
|
add_pending->msg = NULL;
|
|
}
|
|
|
|
static struct l_dbus_message *add_node_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct l_dbus_message_iter iter_uuid, options;
|
|
struct l_dbus_message *reply;
|
|
uint8_t *uuid;
|
|
uint32_t n = 22;
|
|
|
|
l_debug("AddNode request");
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n)
|
|
|| n != 16)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Bad device UUID");
|
|
|
|
/* Allow AddNode to cancel Scanning if from the same node */
|
|
if (scan_node) {
|
|
if (scan_node != node)
|
|
return dbus_error(msg, MESH_ERROR_BUSY, NULL);
|
|
|
|
scan_cancel(NULL, node);
|
|
}
|
|
|
|
/* Invoke Prov Initiator */
|
|
add_pending = l_new(struct add_data, 1);
|
|
memcpy(add_pending->uuid, uuid, 16);
|
|
add_pending->node = node;
|
|
add_pending->agent = node_get_agent(node);
|
|
|
|
if (!node_is_provisioner(node) || (add_pending->agent == NULL)) {
|
|
l_debug("Provisioner: %d", node_is_provisioner(node));
|
|
l_debug("Agent: %p", add_pending->agent);
|
|
reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED,
|
|
"Missing Interfaces");
|
|
goto fail;
|
|
}
|
|
|
|
add_pending->msg = l_dbus_message_ref(msg);
|
|
initiator_start(PB_ADV, uuid, 99, 60, add_pending->agent, add_start,
|
|
add_data_get, add_cmplt, node, add_pending);
|
|
|
|
add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus,
|
|
node_get_owner(node),
|
|
prov_disc_cb, NULL, NULL);
|
|
|
|
return NULL;
|
|
fail:
|
|
l_free(add_pending);
|
|
add_pending = NULL;
|
|
return reply;
|
|
}
|
|
|
|
|
|
static struct l_dbus_message *import_node_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct l_dbus_message_iter iter_key;
|
|
uint16_t primary;
|
|
uint8_t num_ele;
|
|
uint8_t *key;
|
|
uint32_t n;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qyay", &primary, &num_ele,
|
|
&iter_key))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n)
|
|
|| n != 16)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Bad device key");
|
|
|
|
if (!keyring_put_remote_dev_key(node, primary, num_ele, key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *delete_node_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct mesh_net *net = node_get_net(node);
|
|
uint16_t primary;
|
|
uint8_t num_ele;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qy", &primary, &num_ele))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (mesh_net_is_local_address(net, primary, num_ele))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Cannot remove local device key");
|
|
|
|
keyring_del_remote_dev_key(node, primary, num_ele);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info,
|
|
const uint8_t *data, uint16_t len)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct l_dbus_message_builder *builder;
|
|
struct l_dbus_message *msg;
|
|
struct l_dbus *dbus;
|
|
int16_t rssi;
|
|
|
|
if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00)
|
|
return;
|
|
|
|
if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) {
|
|
if (info->rssi <= scan_rssi)
|
|
return;
|
|
}
|
|
|
|
memcpy(scan_uuid, data + 2, sizeof(scan_uuid));
|
|
scan_rssi = info->rssi;
|
|
rssi = info->rssi;
|
|
|
|
dbus = dbus_get_bus();
|
|
msg = l_dbus_message_new_method_call(dbus, node_get_owner(node),
|
|
node_get_app_path(node),
|
|
MESH_PROVISIONER_INTERFACE,
|
|
"ScanResult");
|
|
|
|
builder = l_dbus_message_builder_new(msg);
|
|
l_dbus_message_builder_append_basic(builder, 'n', &rssi);
|
|
dbus_append_byte_array(builder, data + 2, len -2);
|
|
l_dbus_message_builder_enter_array(builder, "{sv}");
|
|
/* TODO: populate with options when defined */
|
|
l_dbus_message_builder_leave_array(builder);
|
|
l_dbus_message_builder_finalize(builder);
|
|
l_dbus_message_builder_destroy(builder);
|
|
|
|
l_dbus_send(dbus, msg);
|
|
}
|
|
|
|
static struct l_dbus_message *start_scan_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
uint16_t duration = 0;
|
|
struct mesh_io *io;
|
|
struct mesh_net *net;
|
|
const char *key;
|
|
struct l_dbus_message_iter options, var;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "a{sv}", &options))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
while (l_dbus_message_iter_next_entry(&options, &key, &var)) {
|
|
bool failed = true;
|
|
|
|
if (!strcmp(key, "Seconds")) {
|
|
if (l_dbus_message_iter_get_variant(&var, "q",
|
|
&duration)) {
|
|
failed = false;
|
|
}
|
|
}
|
|
|
|
if (failed)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Invalid options");
|
|
}
|
|
|
|
if (scan_node && scan_node != node)
|
|
return dbus_error(msg, MESH_ERROR_BUSY, NULL);
|
|
|
|
if (!node_is_provisioner(node))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (scan_timeout)
|
|
l_timeout_remove(scan_timeout);
|
|
|
|
memset(scan_uuid, 0, sizeof(scan_uuid));
|
|
scan_rssi = -128;
|
|
scan_timeout = NULL;
|
|
net = node_get_net(node);
|
|
io = mesh_net_get_io(net);
|
|
scan_node = node;
|
|
mesh_io_register_recv_cb(io, prvb, sizeof(prvb),
|
|
prov_beacon_recv, node);
|
|
|
|
if (duration)
|
|
scan_timeout = l_timeout_create(duration, scan_cancel,
|
|
node, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)) || !node_is_provisioner(node))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (scan_node) {
|
|
if (scan_node != node)
|
|
return dbus_error(msg, MESH_ERROR_BUSY, NULL);
|
|
|
|
scan_cancel(NULL, node);
|
|
}
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *store_new_subnet(struct mesh_node *node,
|
|
struct l_dbus_message *msg,
|
|
uint16_t net_idx, uint8_t *new_key)
|
|
{
|
|
struct keyring_net_key key;
|
|
|
|
if (net_idx > MAX_KEY_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (keyring_get_net_key(node, net_idx, &key)) {
|
|
/* Allow redundant calls only if key values match */
|
|
if (!memcmp(key.old_key, new_key, 16))
|
|
return l_dbus_message_new_method_return(msg);
|
|
|
|
return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, NULL);
|
|
}
|
|
|
|
memcpy(key.old_key, new_key, 16);
|
|
memcpy(key.new_key, new_key, 16);
|
|
key.net_idx = net_idx;
|
|
key.phase = KEY_REFRESH_PHASE_NONE;
|
|
|
|
if (!keyring_put_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *create_subnet_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
uint8_t key[16];
|
|
uint16_t net_idx;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "q", &net_idx) ||
|
|
net_idx == PRIMARY_NET_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
/* Generate key and store */
|
|
l_getrandom(key, sizeof(key));
|
|
|
|
return store_new_subnet(node, msg, net_idx, key);
|
|
}
|
|
|
|
static struct l_dbus_message *update_subnet_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct keyring_net_key key;
|
|
uint16_t net_idx;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "q", &net_idx) ||
|
|
net_idx > MAX_KEY_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!keyring_get_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL);
|
|
|
|
switch (key.phase) {
|
|
case KEY_REFRESH_PHASE_NONE:
|
|
/* Generate Key and update phase */
|
|
l_getrandom(key.new_key, sizeof(key.new_key));
|
|
key.phase = KEY_REFRESH_PHASE_ONE;
|
|
|
|
if (!keyring_put_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
/* Fall Through */
|
|
|
|
case KEY_REFRESH_PHASE_ONE:
|
|
/* Allow redundant calls to start Key Refresh */
|
|
return l_dbus_message_new_method_return(msg);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* All other phases mean KR already in progress over-the-air */
|
|
return dbus_error(msg, MESH_ERROR_IN_PROGRESS,
|
|
"Key Refresh in progress");
|
|
}
|
|
|
|
static struct l_dbus_message *delete_subnet_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
uint16_t net_idx;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "q", &net_idx) ||
|
|
net_idx > MAX_KEY_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
keyring_del_net_key(node, net_idx);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *import_subnet_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct l_dbus_message_iter iter_key;
|
|
uint16_t net_idx;
|
|
uint8_t *key;
|
|
uint32_t n;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qay", &net_idx, &iter_key))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n)
|
|
|| n != 16)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Bad network key");
|
|
|
|
return store_new_subnet(node, msg, net_idx, key);
|
|
}
|
|
|
|
static struct l_dbus_message *store_new_appkey(struct mesh_node *node,
|
|
struct l_dbus_message *msg,
|
|
uint16_t net_idx, uint16_t app_idx,
|
|
uint8_t *new_key)
|
|
{
|
|
struct keyring_net_key net_key;
|
|
struct keyring_app_key app_key;
|
|
|
|
if (net_idx > MAX_KEY_IDX || app_idx > MAX_KEY_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!keyring_get_net_key(node, net_idx, &net_key))
|
|
return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST,
|
|
"Bound net key not found");
|
|
|
|
if (keyring_get_app_key(node, app_idx, &app_key)) {
|
|
/* Allow redundant calls with identical values */
|
|
if (!memcmp(app_key.old_key, new_key, 16) &&
|
|
app_key.net_idx == net_idx)
|
|
return l_dbus_message_new_method_return(msg);
|
|
|
|
return dbus_error(msg, MESH_ERROR_ALREADY_EXISTS, NULL);
|
|
}
|
|
|
|
memcpy(app_key.old_key, new_key, 16);
|
|
memcpy(app_key.new_key, new_key, 16);
|
|
app_key.net_idx = net_idx;
|
|
app_key.app_idx = app_idx;
|
|
|
|
if (!keyring_put_app_key(node, app_idx, net_idx, &app_key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *create_appkey_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
uint16_t net_idx, app_idx;
|
|
uint8_t key[16];
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qq", &net_idx, &app_idx))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
l_getrandom(key, sizeof(key));
|
|
|
|
return store_new_appkey(node, msg, net_idx, app_idx, key);
|
|
}
|
|
|
|
static struct l_dbus_message *update_appkey_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct keyring_net_key net_key;
|
|
struct keyring_app_key app_key;
|
|
uint16_t app_idx;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "q", &app_idx) ||
|
|
app_idx > MAX_KEY_IDX)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!keyring_get_app_key(node, app_idx, &app_key) ||
|
|
!keyring_get_net_key(node, app_key.net_idx, &net_key))
|
|
return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL);
|
|
|
|
if (net_key.phase != KEY_REFRESH_PHASE_ONE)
|
|
return dbus_error(msg, MESH_ERROR_FAILED, "Invalid Phase");
|
|
|
|
/* Generate Key if in acceptable phase */
|
|
l_getrandom(app_key.new_key, sizeof(app_key.new_key));
|
|
|
|
if (!keyring_put_app_key(node, app_idx, app_key.net_idx, &app_key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *delete_appkey_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
uint16_t app_idx;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "q", &app_idx))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
keyring_del_app_key(node, app_idx);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *import_appkey_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct l_dbus_message_iter iter_key;
|
|
uint16_t net_idx, app_idx;
|
|
uint8_t *key;
|
|
uint32_t n;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qqay", &net_idx, &app_idx,
|
|
&iter_key))
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!l_dbus_message_iter_get_fixed_array(&iter_key, &key, &n)
|
|
|| n != 16)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
|
|
"Bad application key");
|
|
|
|
return store_new_appkey(node, msg, net_idx, app_idx, key);
|
|
}
|
|
|
|
static struct l_dbus_message *set_key_phase_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
struct mesh_node *node = user_data;
|
|
struct keyring_net_key key;
|
|
uint16_t net_idx;
|
|
uint8_t phase;
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
if (!l_dbus_message_get_arguments(msg, "qy", &net_idx, &phase) ||
|
|
phase == KEY_REFRESH_PHASE_ONE ||
|
|
phase > KEY_REFRESH_PHASE_THREE)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (!keyring_get_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST, NULL);
|
|
|
|
/* Canceling Key Refresh only valid from Phase One */
|
|
if (phase == KEY_REFRESH_PHASE_NONE &&
|
|
key.phase >= KEY_REFRESH_PHASE_TWO)
|
|
return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
|
|
|
|
if (phase == KEY_REFRESH_PHASE_THREE) {
|
|
|
|
/* If we are already in Phase None, then nothing to do */
|
|
if (key.phase == KEY_REFRESH_PHASE_NONE)
|
|
return l_dbus_message_new_method_return(msg);
|
|
|
|
memcpy(key.old_key, key.new_key, 16);
|
|
key.phase = KEY_REFRESH_PHASE_THREE;
|
|
|
|
if (!keyring_put_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
if (!keyring_finalize_app_keys(node, net_idx))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
key.phase = KEY_REFRESH_PHASE_NONE;
|
|
} else
|
|
key.phase = phase;
|
|
|
|
if (!keyring_put_net_key(node, net_idx, &key))
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
|
|
return l_dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static struct l_dbus_message *export_keys_call(struct l_dbus *dbus,
|
|
struct l_dbus_message *msg,
|
|
void *user_data)
|
|
{
|
|
const char *sender = l_dbus_message_get_sender(msg);
|
|
struct l_dbus_message_builder *builder;
|
|
struct l_dbus_message *reply;
|
|
struct mesh_node *node = user_data;
|
|
|
|
l_debug("Export Keys");
|
|
|
|
if (strcmp(sender, node_get_owner(node)))
|
|
return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
|
|
|
|
reply = l_dbus_message_new_method_return(msg);
|
|
builder = l_dbus_message_builder_new(reply);
|
|
|
|
l_dbus_message_builder_enter_array(builder, "{sv}");
|
|
|
|
if (!keyring_build_export_keys_reply(node, builder)) {
|
|
l_dbus_message_builder_destroy(builder);
|
|
l_dbus_message_unref(reply);
|
|
return dbus_error(msg, MESH_ERROR_FAILED, NULL);
|
|
}
|
|
|
|
l_dbus_message_builder_leave_array(builder);
|
|
l_dbus_message_builder_finalize(builder);
|
|
l_dbus_message_builder_destroy(builder);
|
|
|
|
return reply;
|
|
}
|
|
|
|
static void setup_management_interface(struct l_dbus_interface *iface)
|
|
{
|
|
l_dbus_interface_method(iface, "AddNode", 0, add_node_call, "",
|
|
"aya{sv}", "uuid", "options");
|
|
l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call,
|
|
"", "qyay", "primary", "count", "dev_key");
|
|
l_dbus_interface_method(iface, "DeleteRemoteNode", 0, delete_node_call,
|
|
"", "qy", "primary", "count");
|
|
l_dbus_interface_method(iface, "UnprovisionedScan", 0, start_scan_call,
|
|
"", "a{sv}", "options");
|
|
l_dbus_interface_method(iface, "UnprovisionedScanCancel", 0,
|
|
cancel_scan_call, "", "");
|
|
l_dbus_interface_method(iface, "CreateSubnet", 0, create_subnet_call,
|
|
"", "q", "net_index");
|
|
l_dbus_interface_method(iface, "UpdateSubnet", 0, update_subnet_call,
|
|
"", "q", "net_index");
|
|
l_dbus_interface_method(iface, "DeleteSubnet", 0, delete_subnet_call,
|
|
"", "q", "net_index");
|
|
l_dbus_interface_method(iface, "ImportSubnet", 0, import_subnet_call,
|
|
"", "qay", "net_index", "net_key");
|
|
l_dbus_interface_method(iface, "CreateAppKey", 0, create_appkey_call,
|
|
"", "qq", "net_index", "app_index");
|
|
l_dbus_interface_method(iface, "UpdateAppKey", 0, update_appkey_call,
|
|
"", "q", "app_index");
|
|
l_dbus_interface_method(iface, "DeleteAppKey", 0, delete_appkey_call,
|
|
"", "q", "app_index");
|
|
l_dbus_interface_method(iface, "ImportAppKey", 0, import_appkey_call,
|
|
"", "qqay", "net_index",
|
|
"app_index", "app_key");
|
|
l_dbus_interface_method(iface, "SetKeyPhase", 0, set_key_phase_call, "",
|
|
"qy", "net_index", "phase");
|
|
l_dbus_interface_method(iface, "ExportKeys", 0, export_keys_call,
|
|
"a(qaya{sv})a(qay)", "",
|
|
"net_keys", "dev_keys");
|
|
}
|
|
|
|
bool manager_dbus_init(struct l_dbus *bus)
|
|
{
|
|
if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE,
|
|
setup_management_interface,
|
|
NULL, false)) {
|
|
l_info("Unable to register %s interface",
|
|
MESH_MANAGEMENT_INTERFACE);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void manager_scan_cancel(struct mesh_node *node)
|
|
{
|
|
if (scan_node != node)
|
|
return;
|
|
|
|
scan_cancel(NULL, node);
|
|
}
|