2023-03-03 01:27:47

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 1/9] shared/crypto: Add bt_crypto_sirk

From: Luiz Augusto von Dentz <[email protected]>

This adds bt_crypto_sirk which attempts to generate a unique SIRK using
the following steps:

- Generate a hash (k) using the str as input
- Generate a hash (sirk) using vendor, product, version and source as input
- Encrypt sirk using k as LTK with sef function.
---
src/shared/crypto.c | 40 ++++++++++++++++++++++++++++++++++++++++
src/shared/crypto.h | 3 +++
2 files changed, 43 insertions(+)

diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index 4cb2ea857ea8..5449621b55ea 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -926,3 +926,43 @@ bool bt_crypto_sef(struct bt_crypto *crypto, const uint8_t k[16],

return true;
}
+
+/* Generates a SIRK from a string using the following steps:
+ * - Generate a hash (k) using the str as input
+ * - Generate a hash (sirk) using vendor, product, version and source as input
+ * - Encrypt sirk using k as LTK with sef function.
+ */
+bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
+ uint16_t product, uint16_t version, uint16_t source,
+ uint8_t sirk[16])
+{
+ struct iovec iov[4];
+ uint8_t k[16];
+ uint8_t sirk_plaintext[16];
+
+ if (!crypto)
+ return false;
+
+ iov[0].iov_base = (void *)str;
+ iov[0].iov_len = strlen(str);
+
+ /* Generate a k using the str as input */
+ if (!bt_crypto_gatt_hash(crypto, iov, 1, k))
+ return false;
+
+ iov[0].iov_base = &vendor;
+ iov[0].iov_len = sizeof(vendor);
+ iov[1].iov_base = &product;
+ iov[1].iov_len = sizeof(product);
+ iov[2].iov_base = &version;
+ iov[2].iov_len = sizeof(version);
+ iov[3].iov_base = &source;
+ iov[3].iov_len = sizeof(source);
+
+ /* Generate a sirk using vendor, product, version and source as input */
+ if (!bt_crypto_gatt_hash(crypto, iov, 4, sirk_plaintext))
+ return false;
+
+ /* Encrypt sirk using k as LTK with sef function */
+ return bt_crypto_sef(crypto, k, sirk_plaintext, sirk);
+}
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index fc1ba0c4feeb..d45308abf90a 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -57,3 +57,6 @@ bool bt_crypto_sef(struct bt_crypto *crypto, const uint8_t k[16],
const uint8_t sirk[16], uint8_t out[16]);
bool bt_crypto_sih(struct bt_crypto *crypto, const uint8_t k[16],
const uint8_t r[3], uint8_t hash[3]);
+bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
+ uint16_t product, uint16_t version, uint16_t source,
+ uint8_t sirk[16]);
--
2.39.2



2023-03-03 01:27:48

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 2/9] shared/ad: Add RSI data type

From: Luiz Augusto von Dentz <[email protected]>

This adds BT_AD_CSIP_RSI advertising data type.
---
src/shared/ad.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/src/shared/ad.h b/src/shared/ad.h
index feb712f508cf..b100a6796109 100644
--- a/src/shared/ad.h
+++ b/src/shared/ad.h
@@ -57,6 +57,7 @@
#define BT_AD_MESH_PROV 0x29
#define BT_AD_MESH_DATA 0x2a
#define BT_AD_MESH_BEACON 0x2b
+#define BT_AD_CSIP_RSI 0x2e
#define BT_AD_3D_INFO_DATA 0x3d
#define BT_AD_MANUFACTURER_DATA 0xff

--
2.39.2


2023-03-03 01:27:50

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 3/9] doc: Add set-api

From: Luiz Augusto von Dentz <[email protected]>

This adds set-api.rst which documents DeviceSet interface.
---
doc/set-api.rst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 doc/set-api.rst

diff --git a/doc/set-api.rst b/doc/set-api.rst
new file mode 100644
index 000000000000..17c5f93b6b86
--- /dev/null
+++ b/doc/set-api.rst
@@ -0,0 +1,53 @@
+=====================================
+BlueZ D-Bus DeviceSet API description
+=====================================
+
+
+DeviceSet interface
+===================
+
+Service org.bluez
+Interface org.bluez.DeviceSet1
+Object path [variable prefix]/{hci0,hci1,...}/set_{sirk}
+
+Methods
+=======
+
+**void Connect() [experimental]**
+
+ Connects all **devices** members of the set, each member is
+ connected in sequence as they were added/loaded following the
+ same proceedure as described in **Device1.Connect**.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.AlreadyConnected
+
+**void Disconnect() [experimental]**
+
+ Disconnects all **devices** members of the set, each member is
+ disconnected in sequence as they were connected following the
+ same proceedure as described in **Device1.Disconnect**.
+
+ Possible errors: org.bluez.Error.NotConnected
+
+Properties
+==========
+
+**bool AutoConnect [read-write, experimental]**
+
+ Indicates if the **devices** members of the set shall be automatically
+ connected once any of its members is connected.
+
+**array(object) Devices [ready-only, experimental]**
+
+ List of devices objects that are members of the set.
+
+**byte Size [read-only, experimental]**
+
+ Set members size.
+
+**byte Rank [read-only, experimental]**
+
+ Set members rank.
--
2.39.2


2023-03-03 01:27:54

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 5/9] core: Check if device has RSI

From: Luiz Augusto von Dentz <[email protected]>

This checks if device is advertising an RSI and if so disregards if it
is not discoverable since other members can be.
---
src/adapter.c | 4 ++--
src/eir.c | 3 +++
src/eir.h | 2 ++
3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index ae0eb364bad5..7947160a6c5c 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -7122,7 +7122,7 @@ void btd_adapter_update_found_device(struct btd_adapter *adapter,

dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
if (!dev) {
- if (!discoverable && !monitoring) {
+ if (!discoverable && !monitoring && !eir_data.rsi) {
eir_data_free(&eir_data);
return;
}
@@ -7169,7 +7169,7 @@ void btd_adapter_update_found_device(struct btd_adapter *adapter,
/* If there is no matched Adv monitors, don't continue if not
* discoverable or if active discovery filter don't match.
*/
- if (!monitoring && (!discoverable ||
+ if (!eir_data.rsi && !monitoring && (!discoverable ||
(adapter->filtered_discovery && !is_filter_match(
adapter->discovery_list, &eir_data, rssi)))) {
eir_data_free(&eir_data);
diff --git a/src/eir.c b/src/eir.c
index 2f9ee036ffd5..52152c0d7f52 100644
--- a/src/eir.c
+++ b/src/eir.c
@@ -236,6 +236,9 @@ static void eir_parse_data(struct eir_data *eir, uint8_t type,
memcpy(ad->data, data, len);

eir->data_list = g_slist_append(eir->data_list, ad);
+
+ if (type == EIR_CSIP_RSI)
+ eir->rsi = true;
}

void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
diff --git a/src/eir.h b/src/eir.h
index 6154e23ec266..a4bf5fbd33f3 100644
--- a/src/eir.h
+++ b/src/eir.h
@@ -37,6 +37,7 @@
#define EIR_SVC_DATA32 0x20 /* LE: Service data, 32-bit UUID */
#define EIR_SVC_DATA128 0x21 /* LE: Service data, 128-bit UUID */
#define EIR_TRANSPORT_DISCOVERY 0x26 /* Transport Discovery Service */
+#define EIR_CSIP_RSI 0x2e /* Resolvable Set Identifier */
#define EIR_MANUFACTURER_DATA 0xFF /* Manufacturer Specific Data */

/* Flags Descriptions */
@@ -76,6 +77,7 @@ struct eir_data {
uint32_t class;
uint16_t appearance;
bool name_complete;
+ bool rsi;
int8_t tx_power;
uint8_t *hash;
uint8_t *randomizer;
--
2.39.2


2023-03-03 01:27:54

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 4/9] core: Add initial implementation of DeviceSet interface

From: Luiz Augusto von Dentz <[email protected]>

This adds the initial implementation of DeviceSet interface as
documented in doc/set-api.rst.
---
Makefile.am | 3 +-
src/adapter.c | 6 +-
src/device.c | 124 ++++++++++++++++++-
src/device.h | 11 +-
src/set.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/set.h | 14 +++
6 files changed, 482 insertions(+), 11 deletions(-)
create mode 100644 src/set.c
create mode 100644 src/set.h

diff --git a/Makefile.am b/Makefile.am
index 0e8cce249c7d..7b010c8159e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -326,7 +326,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/eir.h src/eir.c \
src/adv_monitor.h src/adv_monitor.c \
src/battery.h src/battery.c \
- src/settings.h src/settings.c
+ src/settings.h src/settings.c \
+ src/set.h src/set.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
diff --git a/src/adapter.c b/src/adapter.c
index 538310c1ddc6..ae0eb364bad5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4387,8 +4387,8 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys)
if (dev) {
device_set_paired(dev, info->bdaddr_type);
device_set_bonded(dev, info->bdaddr_type);
- device_set_ltk_enc_size(dev, info->enc_size);
- device_set_ltk_enc_size(dev, info->enc_size);
+ device_set_ltk(dev, info->val, info->central,
+ info->enc_size);
}
}

@@ -8657,7 +8657,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
device_set_bonded(device, addr->type);
}

- device_set_ltk_enc_size(device, ev->key.enc_size);
+ device_set_ltk(device, ev->key.val, ev->key.central, ev->key.enc_size);

bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
}
diff --git a/src/device.c b/src/device.c
index cb16d37c1ae1..caee2183b9d8 100644
--- a/src/device.c
+++ b/src/device.c
@@ -64,6 +64,7 @@
#include "storage.h"
#include "eir.h"
#include "settings.h"
+#include "set.h"

#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 1
@@ -159,11 +160,24 @@ struct bearer_state {
time_t last_seen;
};

+struct ltk_info {
+ uint8_t key[16];
+ bool central;
+ uint8_t enc_size;
+};
+
struct csrk_info {
uint8_t key[16];
uint32_t counter;
};

+struct sirk_info {
+ uint8_t encrypted;
+ uint8_t key[16];
+ uint8_t size;
+ uint8_t rank;
+};
+
enum {
WAKE_FLAG_DEFAULT = 0,
WAKE_FLAG_ENABLED,
@@ -253,7 +267,8 @@ struct btd_device {

struct csrk_info *local_csrk;
struct csrk_info *remote_csrk;
- uint8_t ltk_enc_size;
+ struct ltk_info *ltk;
+ struct sirk_info *sirk;

sdp_list_t *tmp_records;

@@ -386,6 +401,21 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
}

+static void store_sirk(struct sirk_info *sirk, GKeyFile *key_file)
+{
+ const char *group = "SetIdentityResolvingKey";
+ char key[33];
+ int i;
+
+ for (i = 0; i < 16; i++)
+ sprintf(key + (i * 2), "%2.2X", sirk->key[i]);
+
+ g_key_file_set_boolean(key_file, group, "Encrypted", sirk->encrypted);
+ g_key_file_set_string(key_file, group, "Key", key);
+ g_key_file_set_integer(key_file, group, "Size", sirk->size);
+ g_key_file_set_integer(key_file, group, "Size", sirk->rank);
+}
+
static gboolean store_device_info_cb(gpointer user_data)
{
struct btd_device *device = user_data;
@@ -483,6 +513,9 @@ static gboolean store_device_info_cb(gpointer user_data)
if (device->remote_csrk)
store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey");

+ if (device->sirk)
+ store_sirk(device->sirk, key_file);
+
str = g_key_file_to_data(key_file, &length, NULL);
if (!g_file_set_contents(filename, str, length, &gerr)) {
error("Unable set contents for %s: (%s)", filename,
@@ -806,6 +839,7 @@ static void device_free(gpointer user_data)

g_free(device->local_csrk);
g_free(device->remote_csrk);
+ free(device->ltk);
g_free(device->path);
g_free(device->alias);
free(device->modalias);
@@ -1792,10 +1826,43 @@ bool device_is_disconnecting(struct btd_device *device)
return device->disconn_timer > 0;
}

-void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size)
+void device_set_ltk(struct btd_device *device, const uint8_t val[16],
+ bool central, uint8_t enc_size)
{
- device->ltk_enc_size = enc_size;
- bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
+ if (!device->ltk)
+ device->ltk = new0(struct ltk_info, 1);
+
+ memcpy(device->ltk->key, val, sizeof(device->ltk->key));
+ device->ltk->central = central;
+ device->ltk->enc_size = enc_size;
+ bt_att_set_enc_key_size(device->att, enc_size);
+
+ if (device->sirk && device->sirk->encrypted)
+ btd_set_add_device(device, device->ltk->key, device->sirk->key,
+ device->sirk->size,
+ device->sirk->rank);
+}
+
+bool btd_device_add_set(struct btd_device *device, bool encrypted,
+ uint8_t sirk[16], uint8_t size, uint8_t rank)
+{
+ if (encrypted && !device->ltk)
+ return false;
+
+ if (!device->sirk)
+ device->sirk = new0(struct sirk_info, 1);
+
+ device->sirk->encrypted = encrypted;
+ memcpy(device->sirk->key, sirk, sizeof(device->sirk->key));
+ device->sirk->size = size;
+ device->sirk->rank = rank;
+
+ btd_set_add_device(device, encrypted ? device->ltk->key : NULL, sirk,
+ size, rank);
+
+ store_device_info(device);
+
+ return true;
}

static void device_set_auto_connect(struct btd_device *device, gboolean enable)
@@ -3290,6 +3357,39 @@ fail:
return NULL;
}

+static struct sirk_info *load_sirk(GKeyFile *key_file)
+{
+ const char *group = "SetIdentityResolvingKey";
+ struct sirk_info *sirk;
+ char *str;
+ int i;
+
+ str = g_key_file_get_string(key_file, group, "Key", NULL);
+ if (!str)
+ return NULL;
+
+ sirk = g_new0(struct sirk_info, 1);
+
+ for (i = 0; i < 16; i++) {
+ if (sscanf(str + (i * 2), "%2hhx", &sirk->key[i]) != 1)
+ goto fail;
+ }
+
+
+ sirk->encrypted = g_key_file_get_boolean(key_file, group, "Encrypted",
+ NULL);
+ sirk->size = g_key_file_get_integer(key_file, group, "Size", NULL);
+ sirk->rank = g_key_file_get_integer(key_file, group, "Rank", NULL);
+ g_free(str);
+
+ return sirk;
+
+fail:
+ g_free(str);
+ g_free(sirk);
+ return NULL;
+}
+
static void load_services(struct btd_device *device, char **uuids)
{
char **uuid;
@@ -3430,6 +3530,12 @@ static void load_info(struct btd_device *device, const char *local,

device->local_csrk = load_csrk(key_file, "LocalSignatureKey");
device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey");
+
+ device->sirk = load_sirk(key_file);
+ if (device->sirk && !device->sirk->encrypted)
+ btd_set_add_device(device, NULL, device->sirk->key,
+ device->sirk->size,
+ device->sirk->rank);
}

g_strfreev(techno);
@@ -5207,7 +5313,9 @@ static void gatt_server_init(struct btd_device *device,
return;
}

- bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
+ if (device->ltk)
+ bt_att_set_enc_key_size(device->att, device->ltk->enc_size);
+
bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);

btd_gatt_database_server_connected(database, device->server);
@@ -6928,3 +7036,9 @@ int8_t btd_device_get_volume(struct btd_device *device)
{
return device->volume;
}
+
+void btd_device_foreach_ad(struct btd_device *dev, bt_ad_func_t func,
+ void *data)
+{
+ bt_ad_foreach_data(dev->ad, func, data);
+}
diff --git a/src/device.h b/src/device.h
index 9e81fda9e948..96347ff229cc 100644
--- a/src/device.h
+++ b/src/device.h
@@ -128,8 +128,10 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
bool *remove);
void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
bool device_is_disconnecting(struct btd_device *device);
-void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size);
-
+void device_set_ltk(struct btd_device *device, const uint8_t val[16],
+ bool central, uint8_t enc_size);
+bool btd_device_add_set(struct btd_device *device, bool encrypted,
+ uint8_t sirk[16], uint8_t size, uint8_t rank);
void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type,
uint16_t value);
void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le,
@@ -188,3 +190,8 @@ void btd_device_cleanup(void);

void btd_device_set_volume(struct btd_device *dev, int8_t volume);
int8_t btd_device_get_volume(struct btd_device *dev);
+
+typedef void (*bt_device_ad_func_t)(void *data, void *user_data);
+
+void btd_device_foreach_ad(struct btd_device *dev, bt_device_ad_func_t func,
+ void *data);
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 000000000000..6b3cd82651e6
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus/gdbus.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/ad.h"
+#include "src/shared/crypto.h"
+
+#include "log.h"
+#include "error.h"
+#include "adapter.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "set.h"
+
+static struct queue *set_list;
+
+struct btd_device_set {
+ struct btd_adapter *adapter;
+ char *path;
+ uint8_t sirk[16];
+ uint8_t size;
+ uint8_t rank;
+ bool auto_connect;
+ struct queue *devices;
+ struct btd_device *device;
+};
+
+static DBusMessage *set_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ /* TODO */
+ return NULL;
+}
+
+static DBusMessage *set_connect(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ /* TODO */
+ return NULL;
+}
+
+static const GDBusMethodTable set_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Disconnect", NULL, NULL,
+ set_disconnect) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Connect", NULL, NULL,
+ set_connect) },
+ {}
+};
+
+static gboolean get_auto_connect(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device_set *set = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &set->auto_connect);
+
+ return TRUE;
+}
+
+static void set_auto_connect(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct btd_device_set *set = data;
+ dbus_bool_t b;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &b);
+
+ set->auto_connect = b ? true : false;
+
+ g_dbus_pending_property_success(id);
+}
+
+static void append_device(void *data, void *user_data)
+{
+ struct btd_device *device = data;
+ const char *path = device_get_path(device);
+ DBusMessageIter *entry = user_data;
+
+ dbus_message_iter_append_basic(entry, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static gboolean get_devices(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device_set *set = data;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING,
+ &entry);
+
+ queue_foreach(set->devices, append_device, &entry);
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static gboolean get_size(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device_set *set = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->size);
+
+ return TRUE;
+}
+
+static gboolean get_rank(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device_set *set = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->rank);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable set_properties[] = {
+ { "AutoConnect", "b", get_auto_connect, set_auto_connect, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Devices", "ao", get_devices, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Size", "y", get_size, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Rank", "y", get_rank, NULL, NULL,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ {}
+};
+
+static void set_free(void *data)
+{
+ struct btd_device_set *set = data;
+
+ queue_destroy(set->devices, NULL);
+ free(set);
+}
+
+static struct btd_device_set *set_new(struct btd_device *device,
+ uint8_t sirk[16], uint8_t size,
+ uint8_t rank)
+{
+ struct btd_device_set *set;
+
+ set = new0(struct btd_device_set, 1);
+ set->adapter = device_get_adapter(device);
+ memcpy(set->sirk, sirk, sizeof(set->sirk));
+ set->size = size;
+ set->rank = rank;
+ set->auto_connect = true;
+ set->devices = queue_new();
+ queue_push_tail(set->devices, device);
+ set->path = g_strdup_printf("%s/set_%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x%02x%02x",
+ adapter_get_path(set->adapter),
+ sirk[15], sirk[14], sirk[13], sirk[12],
+ sirk[11], sirk[10], sirk[9], sirk[8],
+ sirk[7], sirk[6], sirk[5], sirk[4],
+ sirk[3], sirk[2], sirk[1], sirk[0]);
+
+ DBG("Creating set %s", set->path);
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ set->path, BTD_DEVICE_SET_INTERFACE,
+ set_methods, NULL,
+ set_properties, set,
+ set_free) == FALSE) {
+ error("Unable to register set interface");
+ set_free(set);
+ return NULL;
+ }
+
+ return set;
+}
+
+static struct btd_device_set *set_find(struct btd_device *device,
+ uint8_t sirk[16])
+{
+ struct btd_adapter *adapter = device_get_adapter(device);
+ const struct queue_entry *entry;
+
+ for (entry = queue_get_entries(set_list); entry; entry = entry->next) {
+ struct btd_device_set *set = entry->data;
+
+ if (set->adapter != adapter)
+ continue;
+
+ if (!memcmp(set->sirk, sirk, sizeof(set->sirk)))
+ return set;
+ }
+
+ return NULL;
+}
+
+static void set_connect_next(struct btd_device_set *set)
+{
+ const struct queue_entry *entry;
+
+ for (entry = queue_get_entries(set->devices); entry;
+ entry = entry->next) {
+ struct btd_device *device = entry->data;
+
+ /* Only connect one at time(?) */
+ if (!device_connect_le(device))
+ return;
+ }
+}
+
+static void set_add(struct btd_device_set *set, struct btd_device *device)
+{
+ /* Check if device is already part of the set then skip to connect */
+ if (queue_find(set->devices, NULL, device))
+ goto done;
+
+ DBG("set %s device %s", set->path, device_get_path(device));
+
+ queue_push_tail(set->devices, device);
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), set->path,
+ BTD_DEVICE_SET_INTERFACE, "Devices");
+
+done:
+ /* Check if set is marked to auto-connect */
+ if (btd_device_is_connected(device) && set->auto_connect)
+ set_connect_next(set);
+}
+
+static void foreach_rsi(void *data, void *user_data)
+{
+ struct bt_ad_data *ad = data;
+ struct btd_device_set *set = user_data;
+ struct bt_crypto *crypto;
+ uint8_t res[3];
+
+ if (ad->type != BT_AD_CSIP_RSI || ad->len < 6)
+ return;
+
+ crypto = bt_crypto_new();
+ if (!crypto)
+ return;
+
+ if (!bt_crypto_sih(crypto, set->sirk, ad->data + 3, res)) {
+ bt_crypto_unref(crypto);
+ return;
+ }
+
+ bt_crypto_unref(crypto);
+
+ if (!memcmp(ad->data, res, sizeof(res)))
+ device_connect_le(set->device);
+}
+
+static void foreach_device(struct btd_device *device, void *data)
+{
+ struct btd_device_set *set = data;
+
+ /* Check if device is already part of the set then skip */
+ if (queue_find(set->devices, NULL, device))
+ return;
+
+ set->device = device;
+
+ btd_device_foreach_ad(device, foreach_rsi, set);
+}
+
+void btd_set_add_device(struct btd_device *device, uint8_t *key,
+ uint8_t sirk[16], uint8_t size, uint8_t rank)
+{
+ struct btd_device_set *set;
+
+ /* In case key has been set it means SIRK is encrypted */
+ if (key) {
+ struct bt_crypto *crypto = bt_crypto_new();
+
+ if (!crypto)
+ return;
+
+ /* sef and sdf are symmetric */
+ bt_crypto_sef(crypto, key, sirk, sirk);
+
+ bt_crypto_unref(crypto);
+ }
+
+ /* Check if DeviceSet already exists */
+ set = set_find(device, sirk);
+ if (set) {
+ set_add(set, device);
+ return;
+ }
+
+ set = set_new(device, sirk, size, rank);
+ if (!set)
+ return;
+
+ if (!set_list)
+ set_list = queue_new();
+
+ queue_push_tail(set_list, set);
+
+ /* Attempt to add devices which have matching RSI */
+ btd_adapter_for_each_device(device_get_adapter(device), foreach_device,
+ set);
+}
diff --git a/src/set.h b/src/set.h
new file mode 100644
index 000000000000..c3831ef9ca66
--- /dev/null
+++ b/src/set.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ *
+ */
+
+#define BTD_DEVICE_SET_INTERFACE "org.bluez.DeviceSet1"
+
+void btd_set_add_device(struct btd_device *device, uint8_t *ltk,
+ uint8_t sirk[16], uint8_t size, uint8_t rank);
--
2.39.2


2023-03-03 01:27:55

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 6/9] main.conf: Add CSIP profile configurable options

From: Sathish Narasimman <[email protected]>

This introduces option to configure main.conf that can be used to
configure co-ordinated set identification profile.
---
src/btd.h | 9 ++++
src/main.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/main.conf | 25 ++++++++++
3 files changed, 158 insertions(+)

diff --git a/src/btd.h b/src/btd.h
index 42cffcde43ca..70051c71c7c1 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -92,6 +92,13 @@ struct btd_defaults {
struct btd_le_defaults le;
};

+struct btd_csis {
+ bool encrypt;
+ uint8_t sirk[16];
+ uint8_t size;
+ uint8_t rank;
+};
+
struct btd_avdtp_opts {
uint8_t session_mode;
uint8_t stream_mode;
@@ -142,6 +149,8 @@ struct btd_opts {
enum jw_repairing_t jw_repairing;

struct btd_advmon_opts advmon;
+
+ struct btd_csis csis;
};

extern struct btd_opts btd_opts;
diff --git a/src/main.c b/src/main.c
index 99d9c508ff91..2a4d9be05d7a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,6 +43,7 @@
#include "shared/mainloop.h"
#include "shared/timeout.h"
#include "shared/queue.h"
+#include "shared/crypto.h"
#include "lib/uuid.h"
#include "shared/util.h"
#include "btd.h"
@@ -60,6 +61,9 @@
#define DEFAULT_TEMPORARY_TIMEOUT 30 /* 30 seconds */
#define DEFAULT_NAME_REQUEST_RETRY_DELAY 300 /* 5 minutes */

+/*CSIP Profile - Server */
+#define DEFAULT_SIRK "761FAE703ED681F0C50B34155B6434FB"
+
#define SHUTDOWN_GRACE_SECONDS 10

struct btd_opts btd_opts;
@@ -146,6 +150,13 @@ static const char *gatt_options[] = {
NULL
};

+static const char *csip_options[] = {
+ "SIRK",
+ "Size",
+ "Rank",
+ NULL
+};
+
static const char *avdtp_options[] = {
"SessionMode",
"StreamMode",
@@ -166,11 +177,55 @@ static const struct group_table {
{ "LE", le_options },
{ "Policy", policy_options },
{ "GATT", gatt_options },
+ { "CSIP", csip_options },
{ "AVDTP", avdtp_options },
{ "AdvMon", advmon_options },
{ }
};

+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+static int8_t check_sirk_alpha_numeric(char *str)
+{
+ int8_t val = 0;
+ char *s = str;
+
+ if (strlen(s) != 32) /* 32 Bytes of Alpha numeric string */
+ return 0;
+
+ for ( ; *s; s++) {
+ if (((*s >= '0') & (*s <= '9'))
+ || ((*s >= 'a') && (*s <= 'z'))
+ || ((*s >= 'A') && (*s <= 'Z'))) {
+ val = 1;
+ } else {
+ val = 0;
+ break;
+ }
+ }
+
+ return val;
+}
+
+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
+{
+ size_t i, len;
+
+ if (!hexstr)
+ return 0;
+
+ len = MIN((strlen(hexstr) / 2), buflen);
+ memset(buf, 0, len);
+
+ for (i = 0; i < len; i++) {
+ if (sscanf(hexstr + (i * 2), "%02hhX", &buf[i]) != 1)
+ continue;
+ }
+
+ return len;
+}

GKeyFile *btd_get_main_conf(void)
{
@@ -652,6 +707,27 @@ static void btd_parse_kernel_experimental(char **list)
}
}

+static bool gen_sirk(const char *str)
+{
+ struct bt_crypto *crypto;
+ int ret;
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ error("Failed to open crypto");
+ return false;
+ }
+
+ ret = bt_crypto_sirk(crypto, str, btd_opts.did_vendor,
+ btd_opts.did_product, btd_opts.did_version,
+ btd_opts.did_source, btd_opts.csis.sirk);
+ if (!ret)
+ error("Failed to generate SIRK");
+
+ bt_crypto_unref(crypto);
+ return ret;
+}
+
static void parse_config(GKeyFile *config)
{
GError *err = NULL;
@@ -939,6 +1015,54 @@ static void parse_config(GKeyFile *config)
btd_opts.gatt_channels = val;
}

+ str = g_key_file_get_string(config, "CSIP", "SIRK", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("CSIS SIRK: %s", str);
+
+ if (strlen(str) == 32 && check_sirk_alpha_numeric(str)) {
+ hex2bin(str, btd_opts.csis.sirk,
+ sizeof(btd_opts.csis.sirk));
+ } else if (!gen_sirk(str))
+ DBG("Unable to generate SIRK from string");
+
+ g_free(str);
+ }
+
+ boolean = g_key_file_get_boolean(config, "CSIP", "SIRK", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("CSIS Encryption: %s", boolean ? "true" : "false");
+
+ btd_opts.csis.encrypt = boolean;
+ }
+
+ val = g_key_file_get_integer(config, "CSIP", "Size", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ val = MIN(val, 0xFF);
+ val = MAX(val, 0);
+ DBG("CSIS Size: %u", val);
+ btd_opts.csis.size = val;
+ }
+
+ val = g_key_file_get_integer(config, "CSIP", "Rank", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ val = MIN(val, 0xFF);
+ val = MAX(val, 0);
+ DBG("CSIS Rank: %u", val);
+ btd_opts.csis.rank = val;
+ }
+
str = g_key_file_get_string(config, "AVDTP", "SessionMode", &err);
if (err) {
DBG("%s", err->message);
diff --git a/src/main.conf b/src/main.conf
index f7ccbb49eb04..235d1a6ed397 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -258,6 +258,31 @@ KernelExperimental = 6fbaf188-05e0-496a-9885-d6ddfdb4e03e
# Default to 3
#Channels = 3

+[CSIS]
+# SIRK - Set Identification Resolution Key which is common for all the
+# sets. They SIRK key is used to identify its sets. This can be any
+# 128 bit value or a string value (e.g. product name) which is then hashed.
+# Possible Values:
+# 16 byte hexadecimal value: 861FAE703ED681F0C50B34155B6434FB
+# String value: "My Product Name"
+# Defaults to none
+#SIRK =
+
+# SIRK Encryption
+# Possible values:
+# yes: Encrypt SIRK when read
+# no: Do not encrypt SIRK when read. (plaintext)
+# Defaults to yes
+#Encryption = yes
+
+# Total no of sets belongs to this Profile
+# Defaults to 0
+#Size = 0
+
+# Rank for the device
+# Defaults to 0
+#Rank = 0
+
[AVDTP]
# AVDTP L2CAP Signalling Channel Mode.
# Possible values:
--
2.39.2


2023-03-03 01:27:57

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 9/9] tools: Add support to generate RSI using SIRK

From: Sathish Narasimman <[email protected]>

The patch helps to generate Resolvable set identifier adv data.
which can be used as ADV data during advertisement.
It will be used to identify the device as part of setmember for
Coordinated set identification profile.
Example:
$<path to advtest/>advtest -i "761FAE703ED681F0C50B34155B6434FB"
SIRK: 761FAE703ED681F0C50B34155B6434FB
RSI: 0x71 0xcb 0xbc 0x7e 0x01 0x84
Random: bccb71
Hash: 84017e
---
tools/advtest.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 78 insertions(+), 2 deletions(-)

diff --git a/tools/advtest.c b/tools/advtest.c
index de036e783325..9ef69ed5124a 100644
--- a/tools/advtest.c
+++ b/tools/advtest.c
@@ -13,6 +13,13 @@
#include <config.h>
#endif

+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
#include <getopt.h>

#include "lib/bluetooth.h"
@@ -32,6 +39,9 @@
"\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
#define SCAN_IRK "\xfa\x73\x09\x11\x3f\x03\x37\x0f" \
"\xf4\xf9\x93\x1e\xf9\xa3\x63\xa6"
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif

static struct mgmt *mgmt;
static uint16_t index1 = MGMT_INDEX_NONE;
@@ -43,13 +53,73 @@ static struct bt_hci *scan_dev;

static void print_rpa(const uint8_t addr[6])
{
- printf(" Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ printf(" RSI:\t0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
addr[5], addr[4], addr[3],
addr[2], addr[1], addr[0]);
printf(" Random: %02x%02x%02x\n", addr[3], addr[4], addr[5]);
printf(" Hash: %02x%02x%02x\n", addr[0], addr[1], addr[2]);
}

+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
+{
+ size_t i, len;
+
+ len = MIN((strlen(hexstr) / 2), buflen);
+ memset(buf, 0, len);
+
+ for (i = 0; i < len; i++)
+ if (sscanf(hexstr + (i * 2), "%02hhX", &buf[i]) != 1)
+ continue;
+
+
+ return len;
+}
+
+static bool get_random_bytes(void *buf, size_t num_bytes)
+{
+ ssize_t len;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ len = read(fd, buf, num_bytes);
+
+ close(fd);
+
+ if (len < 0)
+ return false;
+
+ return true;
+}
+
+static void generate_rsi(char *val)
+{
+ uint8_t sirk[16], hash[3];
+ uint8_t rsi[6] = {0};
+
+ hex2bin(val, sirk, sizeof(sirk));
+
+ get_random_bytes(&rsi[3], 3);
+
+ rsi[5] &= 0x3f; /* Clear 2 msb */
+ rsi[5] |= 0x40; /* Set 2nd msb */
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ fprintf(stderr, "Failed to open crypto interface\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_crypto_ah(crypto, sirk, rsi + 3, hash);
+ memcpy(rsi, hash, 3);
+
+ print_rpa(rsi);
+}
+
+
static void scan_le_adv_report(const void *data, uint8_t size,
void *user_data)
{
@@ -351,9 +421,11 @@ static void usage(void)
printf("\tadvtest [options]\n");
printf("options:\n"
"\t-h, --help Show help options\n");
+ printf(" \t-i <128bit SIRK>, Generate RSI ADV Data\n");
}

static const struct option main_options[] = {
+ { "hash", no_argument, NULL, 'i' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
@@ -366,11 +438,15 @@ int main(int argc ,char *argv[])
for (;;) {
int opt;

- opt = getopt_long(argc, argv, "vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
if (opt < 0)
break;

switch (opt) {
+ case 'i':
+ printf("SIRK: %s\n", optarg);
+ generate_rsi(optarg);
+ return EXIT_SUCCESS;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
--
2.39.2


2023-03-03 01:27:58

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 7/9] shared/csip: Add initial code for handling CSIP

From: Sathish Narasimman <[email protected]>

This adds initial code for Coordinated Set Identification Profile.
---
Makefile.am | 1 +
src/shared/csip.c | 866 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/csip.h | 67 ++++
3 files changed, 934 insertions(+)
create mode 100644 src/shared/csip.c
create mode 100644 src/shared/csip.h

diff --git a/Makefile.am b/Makefile.am
index 7b010c8159e9..7ded3ba75138 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -233,6 +233,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/bap.h src/shared/bap.c src/shared/ascs.h \
src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \
src/shared/vcp.c src/shared/vcp.h \
+ src/shared/csip.c src/shared/csip.h \
src/shared/lc3.h src/shared/tty.h

if READLINE
diff --git a/src/shared/csip.c b/src/shared/csip.c
new file mode 100644
index 000000000000..4e7ee709b86c
--- /dev/null
+++ b/src/shared/csip.c
@@ -0,0 +1,866 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "src/shared/timeout.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/crypto.h"
+#include "src/shared/csip.h"
+
+#define DBG(_csip, fmt, arg...) \
+ csip_debug(_csip, "%s:%s() " fmt, __FILE__, __func__, ## arg)
+
+/* SIRK is now hardcoded in the code. This can be moved
+ * to a configuration file. Since the code is to validate
+ * the CSIP use case of set member
+ */
+#define SIRK "761FAE703ED681F0C50B34155B6434FB"
+#define CSIS_SIZE 0x02
+#define CSIS_LOCK 0x01
+#define CSIS_RANK 0x01
+#define CSIS_PLAINTEXT 0x01
+#define CSIS_ENC 0x02
+
+struct bt_csip_db {
+ struct gatt_db *db;
+ struct bt_csis *csis;
+};
+
+struct csis_sirk {
+ uint8_t type;
+ uint8_t val[16];
+} __packed;
+
+struct bt_csis {
+ struct bt_csip_db *cdb;
+ struct csis_sirk *sirk_val;
+ uint8_t size_val;
+ uint8_t lock_val;
+ uint8_t rank_val;
+ struct gatt_db_attribute *service;
+ struct gatt_db_attribute *sirk;
+ struct gatt_db_attribute *size;
+ struct gatt_db_attribute *lock;
+ struct gatt_db_attribute *lock_ccc;
+ struct gatt_db_attribute *rank;
+};
+
+struct bt_csip_cb {
+ unsigned int id;
+ bt_csip_func_t attached;
+ bt_csip_func_t detached;
+ void *user_data;
+};
+
+struct bt_csip_ready {
+ unsigned int id;
+ bt_csip_ready_func_t func;
+ bt_csip_destroy_func_t destroy;
+ void *data;
+};
+
+struct bt_csip {
+ int ref_count;
+ struct bt_csip_db *ldb;
+ struct bt_csip_db *rdb;
+ struct bt_gatt_client *client;
+ struct bt_att *att;
+
+ unsigned int idle_id;
+ struct queue *ready_cbs;
+
+ bt_csip_debug_func_t debug_func;
+ bt_csip_destroy_func_t debug_destroy;
+ void *debug_data;
+
+ bt_csip_ltk_func_t ltk_func;
+ void *ltk_data;
+
+ bt_csip_sirk_func_t sirk_func;
+ void *sirk_data;
+
+ void *user_data;
+};
+
+static struct queue *csip_db;
+static struct queue *csip_cbs;
+static struct queue *sessions;
+
+static void csip_detached(void *data, void *user_data)
+{
+ struct bt_csip_cb *cb = data;
+ struct bt_csip *csip = user_data;
+
+ cb->detached(csip, cb->user_data);
+}
+
+void bt_csip_detach(struct bt_csip *csip)
+{
+ if (!queue_remove(sessions, csip))
+ return;
+
+ bt_gatt_client_unref(csip->client);
+ csip->client = NULL;
+
+ queue_foreach(csip_cbs, csip_detached, csip);
+}
+
+static void csip_db_free(void *data)
+{
+ struct bt_csip_db *cdb = data;
+
+ if (!cdb)
+ return;
+
+ gatt_db_unref(cdb->db);
+
+ free(cdb->csis);
+ free(cdb);
+}
+
+static void csip_ready_free(void *data)
+{
+ struct bt_csip_ready *ready = data;
+
+ if (ready->destroy)
+ ready->destroy(ready->data);
+
+ free(ready);
+}
+
+static void csip_free(void *data)
+{
+ struct bt_csip *csip = data;
+
+ bt_csip_detach(csip);
+
+ csip_db_free(csip->rdb);
+
+ queue_destroy(csip->ready_cbs, csip_ready_free);
+
+ free(csip);
+}
+
+struct bt_att *bt_csip_get_att(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ if (csip->att)
+ return csip->att;
+
+ return bt_gatt_client_get_att(csip->client);
+}
+
+struct bt_csip *bt_csip_ref(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ __sync_fetch_and_add(&csip->ref_count, 1);
+
+ return csip;
+}
+
+static struct bt_csip *bt_csip_ref_safe(struct bt_csip *csip)
+{
+ if (!csip || !csip->ref_count)
+ return NULL;
+
+ return bt_csip_ref(csip);
+}
+
+void bt_csip_unref(struct bt_csip *csip)
+{
+ if (!csip)
+ return;
+
+ if (__sync_sub_and_fetch(&csip->ref_count, 1))
+ return;
+
+ csip_free(csip);
+}
+
+static void csip_debug(struct bt_csip *csip, const char *format, ...)
+{
+ va_list ap;
+
+ if (!csip || !format || !csip->debug_func)
+ return;
+
+ va_start(ap, format);
+ util_debug_va(csip->debug_func, csip->debug_data, format, ap);
+ va_end(ap);
+}
+
+static bool csip_match_att(const void *data, const void *match_data)
+{
+ const struct bt_csip *csip = data;
+ const struct bt_att *att = match_data;
+
+ return bt_csip_get_att((void *)csip) == att;
+}
+
+static bool csis_sirk_enc(struct bt_csis *csis, struct bt_att *att,
+ struct csis_sirk *sirk)
+{
+ struct bt_csip *csip;
+ uint8_t k[16];
+ struct bt_crypto *crypto;
+ bool ret;
+
+ csip = queue_find(sessions, csip_match_att, att);
+ if (!csip)
+ return false;
+
+ if (!csip->ltk_func(csip, k, csip->ltk_data)) {
+ DBG(csip, "Unable to read sef key");
+ return false;
+ }
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ DBG(csip, "Failed to open crypto");
+ return false;
+ }
+
+ ret = bt_crypto_sef(crypto, k, sirk->val, sirk->val);
+ if (!ret)
+ DBG(csip, "Failed to encrypt SIRK using sef");
+
+ bt_crypto_unref(crypto);
+
+ return ret;
+}
+
+static void csis_sirk_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_csis *csis = user_data;
+ struct csis_sirk sirk;
+ struct iovec iov;
+
+ memcpy(&sirk, csis->sirk_val, sizeof(sirk));
+
+ if (sirk.type == BT_CSIP_SIRK_ENCRYPT &&
+ !csis_sirk_enc(csis, att, &sirk)) {
+ gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
+ NULL, 0);
+ return;
+ }
+
+ iov.iov_base = &sirk;
+ iov.iov_len = sizeof(sirk);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
+static void csis_size_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_csis *csis = user_data;
+ struct iovec iov;
+
+ iov.iov_base = &csis->size;
+ iov.iov_len = sizeof(csis->size);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
+static void csis_lock_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value = CSIS_LOCK;
+
+ gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value));
+}
+
+static void csis_lock_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void csis_rank_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value = CSIS_RANK;
+
+ gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value));
+}
+
+static struct bt_csis *csis_new(struct gatt_db *db)
+{
+ struct bt_csis *csis;
+
+ if (!db)
+ return NULL;
+
+ csis = new0(struct bt_csis, 1);
+
+ return csis;
+}
+
+static struct bt_csip_db *csip_db_new(struct gatt_db *db)
+{
+ struct bt_csip_db *cdb;
+
+ if (!db)
+ return NULL;
+
+ cdb = new0(struct bt_csip_db, 1);
+ cdb->db = gatt_db_ref(db);
+
+ if (!csip_db)
+ csip_db = queue_new();
+
+ cdb->csis = csis_new(db);
+ cdb->csis->cdb = cdb;
+
+ queue_push_tail(csip_db, cdb);
+
+ return cdb;
+}
+
+bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data)
+{
+ if (!csip)
+ return false;
+
+ csip->user_data = user_data;
+
+ return true;
+}
+
+static bool csip_db_match(const void *data, const void *match_data)
+{
+ const struct bt_csip_db *cdb = data;
+ const struct gatt_db *db = match_data;
+
+ return (cdb->db == db);
+}
+
+static struct bt_csip_db *csip_get_db(struct gatt_db *db)
+{
+ struct bt_csip_db *cdb;
+
+ cdb = queue_find(csip_db, csip_db_match, db);
+ if (cdb)
+ return cdb;
+
+ return csip_db_new(db);
+}
+
+void bt_csip_add_db(struct gatt_db *db)
+{
+ csip_db_new(db);
+}
+
+bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func,
+ void *user_data, bt_csip_destroy_func_t destroy)
+{
+ if (!csip)
+ return false;
+
+ if (csip->debug_destroy)
+ csip->debug_destroy(csip->debug_data);
+
+ csip->debug_func = func;
+ csip->debug_destroy = destroy;
+ csip->debug_data = user_data;
+
+ return true;
+}
+
+unsigned int bt_csip_register(bt_csip_func_t attached, bt_csip_func_t detached,
+ void *user_data)
+{
+ struct bt_csip_cb *cb;
+ static unsigned int id;
+
+ if (!attached && !detached)
+ return 0;
+
+ if (!csip_cbs)
+ csip_cbs = queue_new();
+
+ cb = new0(struct bt_csip_cb, 1);
+ cb->id = ++id ? id : ++id;
+ cb->attached = attached;
+ cb->detached = detached;
+ cb->user_data = user_data;
+
+ queue_push_tail(csip_cbs, cb);
+
+ return cb->id;
+}
+
+static bool match_id(const void *data, const void *match_data)
+{
+ const struct bt_csip_cb *cb = data;
+ unsigned int id = PTR_TO_UINT(match_data);
+
+ return (cb->id == id);
+}
+
+bool bt_csip_unregister(unsigned int id)
+{
+ struct bt_csip_cb *cb;
+
+ cb = queue_remove_if(csip_cbs, match_id, UINT_TO_PTR(id));
+ if (!cb)
+ return false;
+
+ free(cb);
+
+ return true;
+}
+
+struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb)
+{
+ struct bt_csip *csip;
+ struct bt_csip_db *db;
+
+ if (!ldb)
+ return NULL;
+
+ db = csip_get_db(ldb);
+ if (!db)
+ return NULL;
+
+ csip = new0(struct bt_csip, 1);
+ csip->ldb = db;
+ csip->ready_cbs = queue_new();
+
+ if (!rdb)
+ goto done;
+
+ db = new0(struct bt_csip_db, 1);
+ db->db = gatt_db_ref(rdb);
+
+ csip->rdb = db;
+
+done:
+ bt_csip_ref(csip);
+
+ return csip;
+}
+
+static struct bt_csis *csip_get_csis(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ if (csip->rdb->csis)
+ return csip->rdb->csis;
+
+ csip->rdb->csis = new0(struct bt_csis, 1);
+ csip->rdb->csis->cdb = csip->rdb;
+
+ return csip->rdb->csis;
+}
+
+static void read_sirk(bool success, uint8_t att_ecode, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ struct bt_csis *csis;
+ struct csis_sirk *sirk;
+ struct iovec iov = {
+ .iov_base = (void *)value,
+ .iov_len = length
+ };
+
+ if (!success) {
+ DBG(csip, "Unable to read SIRK: error 0x%02x", att_ecode);
+ return;
+ }
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ sirk = util_iov_pull_mem(&iov, sizeof(*sirk));
+ if (!sirk) {
+ DBG(csip, "Invalid size for SIRK: len %u", length);
+ return;
+ }
+
+ if (!csis->sirk_val)
+ csis->sirk_val = new0(struct csis_sirk, 1);
+
+ memcpy(csis->sirk_val, sirk, sizeof(*sirk));
+}
+
+static void read_size(bool success, uint8_t att_ecode, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ struct bt_csis *csis;
+
+ if (!success) {
+ DBG(csip, "Unable to read Size: error 0x%02x", att_ecode);
+ return;
+ }
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ csis->size_val = *value;
+}
+
+static void read_rank(bool success, uint8_t att_ecode, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ struct bt_csis *csis;
+
+ if (!success) {
+ DBG(csip, "Unable to read Rank: error 0x%02x", att_ecode);
+ return;
+ }
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ csis->rank_val = *value;
+}
+
+static void csip_notify_ready(struct bt_csip *csip)
+{
+ const struct queue_entry *entry;
+
+ if (!bt_csip_ref_safe(csip))
+ return;
+
+ for (entry = queue_get_entries(csip->ready_cbs); entry;
+ entry = entry->next) {
+ struct bt_csip_ready *ready = entry->data;
+
+ ready->func(csip, ready->data);
+ }
+
+ bt_csip_unref(csip);
+}
+
+static void foreach_csis_char(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid, uuid_sirk, uuid_size, uuid_rank;
+ struct bt_csis *csis;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+ NULL, NULL, &uuid))
+ return;
+
+ bt_uuid16_create(&uuid_sirk, CS_SIRK);
+ bt_uuid16_create(&uuid_size, CS_SIZE);
+ bt_uuid16_create(&uuid_size, CS_RANK);
+
+ if (!bt_uuid_cmp(&uuid, &uuid_sirk)) {
+ DBG(csip, "SIRK found: handle 0x%04x", value_handle);
+
+ csis = csip_get_csis(csip);
+ if (!csis || csis->sirk)
+ return;
+
+ csis->sirk = attr;
+
+ bt_gatt_client_read_value(csip->client, value_handle, read_sirk,
+ csip, NULL);
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_size)) {
+ DBG(csip, "Size found: handle 0x%04x", value_handle);
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ csis->size = attr;
+
+ bt_gatt_client_read_value(csip->client, value_handle, read_size,
+ csip, NULL);
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_rank)) {
+ DBG(csip, "Rank found: handle 0x%04x", value_handle);
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ csis->rank = attr;
+
+ bt_gatt_client_read_value(csip->client, value_handle, read_rank,
+ csip, NULL);
+ }
+}
+static void foreach_csis_service(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ struct bt_csis *csis = csip_get_csis(csip);
+
+ csis->service = attr;
+
+ gatt_db_service_set_claimed(attr, true);
+
+ gatt_db_service_foreach_char(attr, foreach_csis_char, csip);
+}
+
+static void csip_idle(void *data)
+{
+ struct bt_csip *csip = data;
+
+ csip->idle_id = 0;
+
+ csip_notify_ready(csip);
+}
+
+bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client)
+{
+ bt_uuid_t uuid;
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, csip);
+
+ if (!client)
+ return true;
+
+ if (csip->client)
+ return false;
+
+ csip->client = bt_gatt_client_clone(client);
+ if (!csip->client)
+ return false;
+
+ csip->idle_id = bt_gatt_client_idle_register(csip->client, csip_idle,
+ csip, NULL);
+
+ bt_uuid16_create(&uuid, CSIS_UUID);
+ gatt_db_foreach_service(csip->rdb->db, &uuid, foreach_csis_service,
+ csip);
+
+ return true;
+}
+
+static struct csis_sirk *sirk_new(struct bt_csis *csis, struct gatt_db *db,
+ uint8_t type, uint8_t k[16],
+ uint8_t size, uint8_t rank)
+{
+ struct csis_sirk *sirk;
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *cas;
+
+ if (!csis)
+ return NULL;
+
+ if (csis->sirk)
+ sirk = csis->sirk_val;
+ else
+ sirk = new0(struct csis_sirk, 1);
+
+ sirk->type = type;
+ memcpy(sirk->val, k, sizeof(sirk->val));
+ csis->sirk_val = sirk;
+ csis->size_val = size;
+ csis->lock_val = 1;
+ csis->rank_val = rank;
+
+ /* Check if service already active as that means the attributes have
+ * already been registered.
+ */
+ if (gatt_db_service_get_active(csis->service))
+ return sirk;
+
+ /* Populate DB with CSIS attributes */
+ bt_uuid16_create(&uuid, CSIS_UUID);
+ csis->service = gatt_db_add_service(db, &uuid, true, 10);
+
+ bt_uuid16_create(&uuid, CS_SIRK);
+ csis->sirk = gatt_db_service_add_characteristic(csis->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_sirk_read, NULL,
+ csis);
+
+ bt_uuid16_create(&uuid, CS_SIZE);
+ csis->size = gatt_db_service_add_characteristic(csis->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_size_read, NULL,
+ csis);
+
+ /* Lock */
+ bt_uuid16_create(&uuid, CS_LOCK);
+ csis->lock = gatt_db_service_add_characteristic(csis->service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ csis_lock_read_cb,
+ csis_lock_write_cb,
+ csis);
+
+ csis->lock_ccc = gatt_db_service_add_ccc(csis->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ /* Rank */
+ bt_uuid16_create(&uuid, CS_RANK);
+ csis->rank = gatt_db_service_add_characteristic(csis->service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_rank_read_cb,
+ NULL, csis);
+
+ /* Add the CAS service */
+ bt_uuid16_create(&uuid, 0x1853);
+ cas = gatt_db_add_service(db, &uuid, true, 2);
+ gatt_db_service_add_included(cas, csis->service);
+ gatt_db_service_set_active(cas, true);
+ gatt_db_service_add_included(cas, csis->service);
+
+ gatt_db_service_set_active(csis->service, true);
+
+ return sirk;
+}
+
+bool bt_csip_set_sirk(struct bt_csip *csip, bool encrypt,
+ uint8_t k[16], uint8_t size, uint8_t rank,
+ bt_csip_ltk_func_t func, void *user_data)
+{
+ uint8_t zero[16] = {};
+ uint8_t type;
+
+ if (!csip || !csip->ldb || !memcmp(k, zero, sizeof(zero)))
+ return false;
+
+ type = encrypt ? BT_CSIP_SIRK_ENCRYPT : BT_CSIP_SIRK_CLEARTEXT;
+
+ /* In case of encrypted type requires sef key function */
+ if (type == BT_CSIP_SIRK_ENCRYPT && !func)
+ return false;
+
+ if (!sirk_new(csip->ldb->csis, csip->ldb->db, type, k, size, rank))
+ return false;
+
+ csip->ltk_func = func;
+ csip->ltk_data = user_data;
+
+ return true;
+}
+
+bool bt_csip_get_sirk(struct bt_csip *csip, uint8_t *type,
+ uint8_t k[16], uint8_t *size, uint8_t *rank)
+{
+ struct bt_csis *csis;
+
+ if (!csip)
+ return false;
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return false;
+
+ if (type)
+ *type = csis->sirk_val->type;
+
+ memcpy(k, csis->sirk_val->val, sizeof(csis->sirk_val->val));
+
+ if (size)
+ *size = csis->size_val;
+
+ if (rank)
+ *rank = csis->rank_val;
+
+ return true;
+}
+
+unsigned int bt_csip_ready_register(struct bt_csip *csip,
+ bt_csip_ready_func_t func, void *user_data,
+ bt_csip_destroy_func_t destroy)
+{
+ struct bt_csip_ready *ready;
+ static unsigned int id;
+
+ if (!csip)
+ return 0;
+
+ ready = new0(struct bt_csip_ready, 1);
+ ready->id = ++id ? id : ++id;
+ ready->func = func;
+ ready->destroy = destroy;
+ ready->data = user_data;
+
+ queue_push_tail(csip->ready_cbs, ready);
+
+ return ready->id;
+}
+
+static bool match_ready_id(const void *data, const void *match_data)
+{
+ const struct bt_csip_ready *ready = data;
+ unsigned int id = PTR_TO_UINT(match_data);
+
+ return (ready->id == id);
+}
+
+bool bt_csip_ready_unregister(struct bt_csip *csip, unsigned int id)
+{
+ struct bt_csip_ready *ready;
+
+ ready = queue_remove_if(csip->ready_cbs, match_ready_id,
+ UINT_TO_PTR(id));
+ if (!ready)
+ return false;
+
+ csip_ready_free(ready);
+
+ return true;
+}
diff --git a/src/shared/csip.h b/src/shared/csip.h
new file mode 100644
index 000000000000..0f8acb1cae82
--- /dev/null
+++ b/src/shared/csip.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ */
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "src/shared/io.h"
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct bt_csip;
+
+enum {
+ BT_CSIP_SIRK_ENCRYPT = 0x00,
+ BT_CSIP_SIRK_CLEARTEXT = 0x01
+};
+
+typedef void (*bt_csip_ready_func_t)(struct bt_csip *csip, void *user_data);
+typedef void (*bt_csip_destroy_func_t)(void *user_data);
+typedef void (*bt_csip_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_csip_func_t)(struct bt_csip *csip, void *user_data);
+typedef bool (*bt_csip_ltk_func_t)(struct bt_csip *csip, uint8_t k[16],
+ void *user_data);
+typedef bool (*bt_csip_sirk_func_t)(struct bt_csip *csip, uint8_t type,
+ uint8_t k[16], uint8_t size, uint8_t rank,
+ void *user_data);
+
+struct bt_csip *bt_csip_ref(struct bt_csip *csip);
+void bt_csip_unref(struct bt_csip *csip);
+
+void bt_csip_add_db(struct gatt_db *db);
+
+bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client);
+void bt_csip_detach(struct bt_csip *csip);
+
+bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func,
+ void *user_data, bt_csip_destroy_func_t destroy);
+
+struct bt_att *bt_csip_get_att(struct bt_csip *csip);
+
+bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data);
+
+/* Session related function */
+unsigned int bt_csip_register(bt_csip_func_t added, bt_csip_func_t removed,
+ void *user_data);
+bool bt_csip_unregister(unsigned int id);
+struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb);
+
+bool bt_csip_set_sirk(struct bt_csip *csip, bool encrypt,
+ uint8_t k[16], uint8_t size, uint8_t rank,
+ bt_csip_ltk_func_t func, void *user_data);
+
+bool bt_csip_get_sirk(struct bt_csip *csip, uint8_t *type,
+ uint8_t k[16], uint8_t *size, uint8_t *rank);
+
+unsigned int bt_csip_ready_register(struct bt_csip *csip,
+ bt_csip_ready_func_t func, void *user_data,
+ bt_csip_destroy_func_t destroy);
+bool bt_csip_ready_unregister(struct bt_csip *csip, unsigned int id);
--
2.39.2


2023-03-03 01:27:59

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [RFC 8/9] profiles: Add initial code for csip plugin

From: Sathish Narasimman <[email protected]>

This adds initial code for csip plugin which handles Coordinated
set identification Profile and Coordinated Set Identification
Service.
---
Makefile.plugins | 5 +
configure.ac | 4 +
profiles/audio/csip.c | 363 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 372 insertions(+)
create mode 100644 profiles/audio/csip.c

diff --git a/Makefile.plugins b/Makefile.plugins
index 20cac384ef44..0f119e8714b7 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -131,3 +131,8 @@ if VCP
builtin_modules += vcp
builtin_sources += profiles/audio/vcp.c
endif
+
+if CSIP
+builtin_modules += csip
+builtin_sources += profiles/audio/csip.c
+endif
diff --git a/configure.ac b/configure.ac
index 515cdf1461eb..6f890110f554 100644
--- a/configure.ac
+++ b/configure.ac
@@ -207,6 +207,10 @@ AC_ARG_ENABLE(vcp, AS_HELP_STRING([--disable-vcp],
[disable VCP profile]), [enable_vcp=${enableval}])
AM_CONDITIONAL(VCP, test "${enable_vcp}" != "no")

+AC_ARG_ENABLE(csip, AS_HELP_STRING([--disable-csip],
+ [disable CSIP profile]), [enable_csip=${enableval}])
+AM_CONDITIONAL(CSIP, test "${enable_csip}" != "no")
+
AC_ARG_ENABLE(tools, AS_HELP_STRING([--disable-tools],
[disable Bluetooth tools]), [enable_tools=${enableval}])
AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
diff --git a/profiles/audio/csip.c b/profiles/audio/csip.c
new file mode 100644
index 000000000000..c273c02b8e76
--- /dev/null
+++ b/profiles/audio/csip.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/csip.h"
+
+#include "btio/btio.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/gatt-database.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/btd.h"
+
+#define CSIS_UUID_STR "00001846-0000-1000-8000-00805f9b34fb"
+
+struct csip_data {
+ struct btd_device *device;
+ struct btd_service *service;
+ struct bt_csip *csip;
+ unsigned int ready_id;
+};
+
+static struct queue *sessions;
+
+static void csip_debug(const char *str, void *user_data)
+{
+ DBG_IDX(0xffff, "%s", str);
+}
+
+static struct csip_data *csip_data_new(struct btd_device *device)
+{
+ struct csip_data *data;
+
+ data = new0(struct csip_data, 1);
+ data->device = device;
+
+ return data;
+}
+
+static bool csip_ltk_read(struct bt_csip *csip, uint8_t k[16], void *user_data)
+{
+ /* TODO: Retrieve LTK using device object */
+ return false;
+}
+
+static void csip_data_add(struct csip_data *data)
+{
+ DBG("data %p", data);
+
+ if (queue_find(sessions, NULL, data)) {
+ error("data %p already added", data);
+ return;
+ }
+
+ bt_csip_set_debug(data->csip, csip_debug, NULL, NULL);
+
+ bt_csip_set_sirk(data->csip, btd_opts.csis.encrypt, btd_opts.csis.sirk,
+ btd_opts.csis.size, btd_opts.csis.rank,
+ csip_ltk_read, data);
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, data);
+
+ if (data->service)
+ btd_service_set_user_data(data->service, data);
+}
+
+static int csip_disconnect(struct btd_service *service)
+{
+ struct csip_data *data = btd_service_get_user_data(service);
+
+ bt_csip_detach(data->csip);
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
+}
+
+static bool match_data(const void *data, const void *match_data)
+{
+ const struct csip_data *vdata = data;
+ const struct bt_csip *csip = match_data;
+
+ return vdata->csip == csip;
+}
+
+static void csip_data_free(struct csip_data *data)
+{
+ if (data->service) {
+ btd_service_set_user_data(data->service, NULL);
+ bt_csip_set_user_data(data->csip, NULL);
+ }
+
+ bt_csip_ready_unregister(data->csip, data->ready_id);
+ bt_csip_unref(data->csip);
+ free(data);
+}
+
+static void csip_data_remove(struct csip_data *data)
+{
+ DBG("data %p", data);
+
+ if (!queue_remove(sessions, data))
+ return;
+
+ csip_data_free(data);
+
+ if (queue_isempty(sessions)) {
+ queue_destroy(sessions, NULL);
+ sessions = NULL;
+ }
+}
+
+static void csip_detached(struct bt_csip *csip, void *user_data)
+{
+ struct csip_data *data;
+
+ DBG("%p", csip);
+
+ data = queue_find(sessions, match_data, csip);
+ if (!data) {
+ error("Unable to find csip session");
+ return;
+ }
+
+ /* If there is a service it means there is CSIS thus we can keep
+ * instance allocated.
+ */
+ if (data->service)
+ return;
+
+ csip_data_remove(data);
+}
+
+static void csip_attached(struct bt_csip *csip, void *user_data)
+{
+ struct csip_data *data;
+ struct bt_att *att;
+ struct btd_device *device;
+
+ DBG("%p", csip);
+
+ data = queue_find(sessions, match_data, csip);
+ if (data)
+ return;
+
+ att = bt_csip_get_att(csip);
+ if (!att)
+ return;
+
+ device = btd_adapter_find_device_by_fd(bt_att_get_fd(att));
+ if (!device) {
+ error("Unable to find device");
+ return;
+ }
+
+ data = csip_data_new(device);
+ data->csip = csip;
+
+ csip_data_add(data);
+
+}
+
+static int csip_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+
+ DBG("CSIP path %s", adapter_get_path(adapter));
+
+ bt_csip_add_db(btd_gatt_database_get_db(database));
+
+ return 0;
+}
+
+static void csip_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ DBG("CSIP remove Adapter");
+}
+
+static int csip_accept(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ struct csip_data *data = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ if (!data) {
+ error("CSIP service not handled by profile");
+ return -EINVAL;
+ }
+
+ if (!bt_csip_attach(data->csip, client)) {
+ error("CSIP unable to attach");
+ return -EINVAL;
+ }
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static void csip_ready(struct bt_csip *csip, void *user_data)
+{
+ struct btd_service *service = user_data;
+ struct btd_device *device = btd_service_get_device(service);
+ uint8_t type, size, rank;
+ uint8_t k[16];
+
+ DBG("csip %p", csip);
+
+ if (!bt_csip_get_sirk(csip, &type, k, &size, &rank)) {
+ error("Unable to read SIRK");
+ return;
+ }
+
+ btd_device_add_set(device, type == BT_CSIP_SIRK_ENCRYPT ? true : false,
+ k, size, rank);
+}
+
+static int csip_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+ struct csip_data *data = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ /* Ignore, if we were probed for this device already */
+ if (data) {
+ error("Profile probed twice for the same device!");
+ return -EINVAL;
+ }
+
+ data = csip_data_new(device);
+ data->service = service;
+
+ data->csip = bt_csip_new(btd_gatt_database_get_db(database),
+ btd_device_get_gatt_db(device));
+ if (!data->csip) {
+ error("Unable to create CSIP instance");
+ free(data);
+ return -EINVAL;
+ }
+
+ csip_data_add(data);
+
+ data->ready_id = bt_csip_ready_register(data->csip, csip_ready, service,
+ NULL);
+
+ bt_csip_set_user_data(data->csip, service);
+
+ return 0;
+}
+
+static void csip_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct csip_data *data;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ data = btd_service_get_user_data(service);
+ if (!data) {
+ error("CSIP service not handled by profile");
+ return;
+ }
+
+ csip_data_remove(data);
+}
+
+static struct btd_profile csip_profile = {
+ .name = "csip",
+ .priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .remote_uuid = CSIS_UUID_STR,
+
+ .device_probe = csip_probe,
+ .device_remove = csip_remove,
+
+ .accept = csip_accept,
+ .disconnect = csip_disconnect,
+
+ .adapter_probe = csip_server_probe,
+ .adapter_remove = csip_server_remove,
+};
+
+static unsigned int csip_id;
+
+static int csip_init(void)
+{
+ if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
+ warn("D-Bus experimental not enabled");
+ return -ENOTSUP;
+ }
+
+ btd_profile_register(&csip_profile);
+ csip_id = bt_csip_register(csip_attached, csip_detached, NULL);
+
+ return 0;
+}
+
+static void csip_exit(void)
+{
+ if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) {
+ btd_profile_unregister(&csip_profile);
+ bt_csip_unregister(csip_id);
+ }
+}
+
+BLUETOOTH_PLUGIN_DEFINE(csip, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ csip_init, csip_exit)
--
2.39.2


2023-03-03 04:30:30

by bluez.test.bot

[permalink] [raw]
Subject: RE: [RFC,1/9] shared/crypto: Add bt_crypto_sirk

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=726293

---Test result---

Test Summary:
CheckPatch FAIL 5.05 seconds
GitLint PASS 2.30 seconds
BuildEll PASS 26.19 seconds
BluezMake PASS 751.94 seconds
MakeCheck PASS 11.07 seconds
MakeDistcheck PASS 147.73 seconds
CheckValgrind PASS 240.16 seconds
CheckSmatch WARNING 321.92 seconds
bluezmakeextell PASS 97.20 seconds
IncrementalBuild PASS 5482.90 seconds
ScanBuild PASS 953.10 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[RFC,1/9] shared/crypto: Add bt_crypto_sirk
WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#84:
- Generate a hash (sirk) using vendor, product, version and source as input

/github/workspace/src/src/13158198.patch total: 0 errors, 1 warnings, 49 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13158198.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


[RFC,6/9] main.conf: Add CSIP profile configurable options
WARNING:STATIC_CONST_CHAR_ARRAY: static const char * array should probably be static const char * const
#145: FILE: src/main.c:153:
+static const char *csip_options[] = {

/github/workspace/src/src/13158203.patch total: 0 errors, 1 warnings, 217 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13158203.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


[RFC,7/9] shared/csip: Add initial code for handling CSIP
WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __packed over __attribute__((packed))
#998: FILE: src/shared/csip.h:16:
+#define __packed __attribute__((packed))

/github/workspace/src/src/13158205.patch total: 0 errors, 1 warnings, 940 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13158205.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
src/shared/crypto.c:271:21: warning: Variable length array is used.src/shared/crypto.c:272:23: warning: Variable length array is used.src/shared/crypto.c:271:21: warning: Variable length array is used.src/shared/crypto.c:272:23: warning: Variable length array is used.src/shared/crypto.c:271:21: warning: Variable length array is used.src/shared/crypto.c:272:23: warning: Variable length array is used.


---
Regards,
Linux Bluetooth

2023-03-04 22:19:58

by Pauli Virtanen

[permalink] [raw]
Subject: Re: [RFC 3/9] doc: Add set-api

Hi Luiz,

to, 2023-03-02 kello 17:27 -0800, Luiz Augusto von Dentz kirjoitti:
> > From: Luiz Augusto von Dentz <[email protected]>
> >
> > This adds set-api.rst which documents DeviceSet interface.
> > ---
> > doc/set-api.rst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 53 insertions(+)
> > create mode 100644 doc/set-api.rst
> >
> > diff --git a/doc/set-api.rst b/doc/set-api.rst
> > new file mode 100644
> > index 000000000000..17c5f93b6b86
> > --- /dev/null
> > +++ b/doc/set-api.rst
> > @@ -0,0 +1,53 @@
> > +=====================================
> > +BlueZ D-Bus DeviceSet API description
> > +=====================================
> > +
> > +
> > +DeviceSet interface
> > +===================
> > +
> > +Service org.bluez
> > +Interface org.bluez.DeviceSet1
> > +Object path [variable prefix]/{hci0,hci1,...}/set_{sirk}
> > +
> > +Methods
> > +=======
> > +
> > +**void Connect() [experimental]**
> > +
> > + Connects all **devices** members of the set, each member is
> > + connected in sequence as they were added/loaded following the
> > + same proceedure as described in **Device1.Connect**.
> > +
> > + Possible errors: org.bluez.Error.NotReady
> > + org.bluez.Error.Failed
> > + org.bluez.Error.InProgress
> > + org.bluez.Error.AlreadyConnected
> > +
> > +**void Disconnect() [experimental]**
> > +
> > + Disconnects all **devices** members of the set, each member is
> > + disconnected in sequence as they were connected following the
> > + same proceedure as described in **Device1.Disconnect**.
> > +
> > + Possible errors: org.bluez.Error.NotConnected
> > +
> > +Properties
> > +==========
> > +
> > +**bool AutoConnect [read-write, experimental]**
> > +
> > + Indicates if the **devices** members of the set shall be automatically
> > + connected once any of its members is connected.
> > +
> > +**array(object) Devices [ready-only, experimental]**
> > +
> > + List of devices objects that are members of the set.
> > +
> > +**byte Size [read-only, experimental]**
> > +
> > + Set members size.
> > +
> > +**byte Rank [read-only, experimental]**
> > +
> > + Set members rank.

It looks like rank is supposed to be a property of each set member
device, not a constant property of the set like size.

See CSIS 5.4: "The value of the Set Member Rank characteristic exposed
by servers that are part of the same Coordinated Set shall be
contiguous integers starting from 0x01 to the size of the Coordinated
Set."

So maybe should be something like

**array(byte) Ranks [read-only, experimental]**

Set member ranks.

--
Pauli Virtanen

2023-03-04 22:38:33

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC 3/9] doc: Add set-api

Hi Pauli,

On Sat, Mar 4, 2023 at 2:19 PM Pauli Virtanen <[email protected]> wrote:
>
> Hi Luiz,
>
> to, 2023-03-02 kello 17:27 -0800, Luiz Augusto von Dentz kirjoitti:
> > > From: Luiz Augusto von Dentz <[email protected]>
> > >
> > > This adds set-api.rst which documents DeviceSet interface.
> > > ---
> > > doc/set-api.rst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++
> > > 1 file changed, 53 insertions(+)
> > > create mode 100644 doc/set-api.rst
> > >
> > > diff --git a/doc/set-api.rst b/doc/set-api.rst
> > > new file mode 100644
> > > index 000000000000..17c5f93b6b86
> > > --- /dev/null
> > > +++ b/doc/set-api.rst
> > > @@ -0,0 +1,53 @@
> > > +=====================================
> > > +BlueZ D-Bus DeviceSet API description
> > > +=====================================
> > > +
> > > +
> > > +DeviceSet interface
> > > +===================
> > > +
> > > +Service org.bluez
> > > +Interface org.bluez.DeviceSet1
> > > +Object path [variable prefix]/{hci0,hci1,...}/set_{sirk}
> > > +
> > > +Methods
> > > +=======
> > > +
> > > +**void Connect() [experimental]**
> > > +
> > > + Connects all **devices** members of the set, each member is
> > > + connected in sequence as they were added/loaded following the
> > > + same proceedure as described in **Device1.Connect**.
> > > +
> > > + Possible errors: org.bluez.Error.NotReady
> > > + org.bluez.Error.Failed
> > > + org.bluez.Error.InProgress
> > > + org.bluez.Error.AlreadyConnected
> > > +
> > > +**void Disconnect() [experimental]**
> > > +
> > > + Disconnects all **devices** members of the set, each member is
> > > + disconnected in sequence as they were connected following the
> > > + same proceedure as described in **Device1.Disconnect**.
> > > +
> > > + Possible errors: org.bluez.Error.NotConnected
> > > +
> > > +Properties
> > > +==========
> > > +
> > > +**bool AutoConnect [read-write, experimental]**
> > > +
> > > + Indicates if the **devices** members of the set shall be automatically
> > > + connected once any of its members is connected.
> > > +
> > > +**array(object) Devices [ready-only, experimental]**
> > > +
> > > + List of devices objects that are members of the set.
> > > +
> > > +**byte Size [read-only, experimental]**
> > > +
> > > + Set members size.
> > > +
> > > +**byte Rank [read-only, experimental]**
> > > +
> > > + Set members rank.
>
> It looks like rank is supposed to be a property of each set member
> device, not a constant property of the set like size.

Yep, Ive been debating putting a property called SetRank into the
Device interface.

> See CSIS 5.4: "The value of the Set Member Rank characteristic exposed
> by servers that are part of the same Coordinated Set shall be
> contiguous integers starting from 0x01 to the size of the Coordinated
> Set."
>
> So maybe should be something like
>
> **array(byte) Ranks [read-only, experimental]**

Or we actually sort the Devices list based on the rank, anyway Ive
been doing some modification like adding the Adapter object to
DeviceSet and a Set property to Device interface to make it easier to
locate its members, Im also planning to add support to the likes of
bluetoothctl.

> Set member ranks.
>
> --
> Pauli Virtanen



--
Luiz Augusto von Dentz

2023-03-05 21:57:34

by Pauli Virtanen

[permalink] [raw]
Subject: Re: [RFC 4/9] core: Add initial implementation of DeviceSet interface

Hi,

to, 2023-03-02 kello 17:27 -0800, Luiz Augusto von Dentz kirjoitti:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This adds the initial implementation of DeviceSet interface as
> documented in doc/set-api.rst.

Tried this with Samsung Galaxy Buds2 Pro, things mostly seems to work.
When pairing, it asks to pair also the other earbud, and when
connecting one it usually also connects the other one.

On DBus, the deviceset appears correctly.

I think on some of the attempts the pairing/connecting of the other set
device did not work properly, but I don't have good information on this
right now and nothing reproducible, will need to test again so just
quick impressions now.

(Right now I'm not able to play audio to both earbuds at the same time.
This is unrelated to this patchset, and probably known issue similar to
multiple CIS to same device. What seems to be happening is the same
that there is a successful "LE Set Connected Isochronous Group
Parameters" creating two connection handles but no following "LE Create
Connected Isochronous Stream" so connect timeouts.)

On one attempt, the other device auto-paired, bluetoothctl asked
"Accept pairing" and the device appeared in "bluetoothctl devices".
Trying to connect to it in bluetoothctl after that caused it to not
connect successfully (just "Attempting to connect to ...") but reason
was unclear.

The device uses the random address scheme where the address changes on
pairing. When I connected the device the first time, I see

[Galaxy Buds2 Pro]# endpoint.list
Endpoint /org/bluez/hci1/dev_69_1E_A2_F9_2D_6B/pac_source0
Endpoint /org/bluez/hci1/dev_69_1E_A2_F9_2D_6B/pac_sink0
Endpoint /org/bluez/hci1/dev_63_BD_D1_39_E8_75/pac_source0
Endpoint /org/bluez/hci1/dev_63_BD_D1_39_E8_75/pac_sink0

which shows the random addresses. Sometimes the device and endpoint
names get the resolved address, but this does not alway happen.
Sometimes only of the devices gets the resolved in the names.

Sometimes after pairing, in bluetoothctl I see three devices, one of
them the old random address of one of the two other devices. Sometimes
it's three devices with one of the addresses listed twice.

A few ASAN crashes:

While connecting device:

bluetoothd[29776]: =================================================================
bluetoothd[29776]: ==29776==ERROR: AddressSanitizer: heap-use-after-free on address 0x6170000001f0 at pc 0x000000670565 bp 0x7ffca7750f50 sp 0x7ffca7750f48
bluetoothd[29776]: READ of size 8 at 0x6170000001f0 thread T0
bluetoothd[29776]: #0 0x670564 in device_connect_le src/device.c:5526
bluetoothd[29776]: #1 0x69f457 in set_connect_next src/set.c:237
bluetoothd[29776]: #2 0x69f7d9 in set_add src/set.c:257
bluetoothd[29776]: #3 0x69fdcf in btd_set_add_device src/set.c:319
bluetoothd[29776]: #4 0x64dcc0 in btd_device_add_set src/device.c:1860
bluetoothd[29776]: #5 0x521c2f in csip_ready profiles/audio/csip.c:264
bluetoothd[29776]: #6 0x7d389e in csip_notify_ready src/shared/csip.c:578
bluetoothd[29776]: #7 0x7d43b6 in csip_idle src/shared/csip.c:659
bluetoothd[29776]: #8 0x7437d1 in idle_notify src/shared/gatt-client.c:171
bluetoothd[29776]: #9 0x713c7b in queue_remove_if src/shared/queue.c:279
bluetoothd[29776]: #10 0x7142af in queue_remove_all src/shared/queue.c:321
bluetoothd[29776]: #11 0x74388b in notify_client_idle src/shared/gatt-client.c:180
bluetoothd[29776]: #12 0x743cb9 in request_unref src/shared/gatt-client.c:199
bluetoothd[29776]: #13 0x732bb1 in destroy_att_send_op src/shared/att.c:209
bluetoothd[29776]: #14 0x739773 in handle_rsp src/shared/att.c:862
bluetoothd[29776]: #15 0x73ae8f in can_read_data src/shared/att.c:1052
bluetoothd[29776]: #16 0x7d7d25 in watch_callback src/shared/io-glib.c:157
bluetoothd[29776]: #17 0x7ff21f9a6c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
bluetoothd[29776]: #18 0x7ff21f9fd117 in g_main_context_iterate.constprop.0 (/lib64/libglib-2.0.so.0+0xad117)
bluetoothd[29776]: #19 0x7ff21f9a624e in g_main_loop_run (/lib64/libglib-2.0.so.0+0x5624e)
bluetoothd[29776]: #20 0x7d8fe5 in mainloop_run src/shared/mainloop-glib.c:66
bluetoothd[29776]: #21 0x7d9d0c in mainloop_run_with_signal src/shared/mainloop-notify.c:188
bluetoothd[29776]: #22 0x55497c in main src/main.c:1428
bluetoothd[29776]: #23 0x7ff21e84a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
bluetoothd[29776]: #24 0x7ff21e84a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8)
bluetoothd[29776]: #25 0x40da34 in _start (/usr/local/stow/bluez-dev/libexec/bluetooth/bluetoothd+0x40da34)
bluetoothd[29776]: 0x6170000001f0 is located 368 bytes inside of 680-byte region [0x617000000080,0x617000000328)
bluetoothd[29776]: freed by thread T0 here:
bluetoothd[29776]: #0 0x7ff21f2b9388 in __interceptor_free.part.0 (/lib64/libasan.so.8+0xb9388)
bluetoothd[29776]: #1 0x7ff21f9ab88c in g_free (/lib64/libglib-2.0.so.0+0x5b88c)
bluetoothd[29776]: #2 0x6fb494 in remove_interface gdbus/object.c:660
bluetoothd[29776]: #3 0x70190f in g_dbus_unregister_interface gdbus/object.c:1394
bluetoothd[29776]: #4 0x67b761 in btd_device_unref src/device.c:6883
bluetoothd[29776]: #5 0x666838 in device_remove src/device.c:4527
bluetoothd[29776]: #6 0x5c0e21 in btd_adapter_remove_device src/adapter.c:1441
bluetoothd[29776]: #7 0x5d269d in remove_device src/adapter.c:3546
bluetoothd[29776]: #8 0x6f7c8a in process_message gdbus/object.c:246
bluetoothd[29776]: #9 0x6ff1c1 in generic_message gdbus/object.c:1072
bluetoothd[29776]: #10 0x7ff21f917d88 in dbus_connection_dispatch (/lib64/libdbus-1.so.3+0x1cd88)
bluetoothd[29776]: previously allocated by thread T0 here:
bluetoothd[29776]: #0 0x7ff21f2ba097 in calloc (/lib64/libasan.so.8+0xba097)
bluetoothd[29776]: #1 0x661074 in device_new src/device.c:4006
bluetoothd[29776]: #2 0x661b34 in device_create_from_storage src/device.c:4072
bluetoothd[29776]: #3 0x5de389 in load_devices src/adapter.c:4930
bluetoothd[29776]: #4 0x6051fe in adapter_register src/adapter.c:9264
bluetoothd[29776]: #5 0x60da49 in read_info_complete src/adapter.c:10159
bluetoothd[29776]: #6 0x71aec3 in request_complete src/shared/mgmt.c:306
bluetoothd[29776]: #7 0x71c271 in can_read_data src/shared/mgmt.c:394
bluetoothd[29776]: #8 0x7d7d25 in watch_callback src/shared/io-glib.c:157
bluetoothd[29776]: #9 0x7ff21f9a6c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
bluetoothd[29776]: SUMMARY: AddressSanitizer: heap-use-after-free src/device.c:5526 in device_connect_le
bluetoothd[29776]: Shadow bytes around the buggy address:
bluetoothd[29776]: 0x0c2e7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bluetoothd[29776]: 0x0c2e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bluetoothd[29776]: 0x0c2e7fff8000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
bluetoothd[29776]: 0x0c2e7fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[29776]: 0x0c2e7fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[29776]: =>0x0c2e7fff8030: fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd
bluetoothd[29776]: 0x0c2e7fff8040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[29776]: 0x0c2e7fff8050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[29776]: 0x0c2e7fff8060: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
bluetoothd[29776]: 0x0c2e7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
bluetoothd[29776]: 0x0c2e7fff8080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bluetoothd[29776]: Shadow byte legend (one shadow byte represents 8 application bytes):
bluetoothd[29776]: Addressable: 00
bluetoothd[29776]: Partially addressable: 01 02 03 04 05 06 07
bluetoothd[29776]: Heap left redzone: fa
bluetoothd[29776]: Freed heap region: fd
bluetoothd[29776]: Stack left redzone: f1
bluetoothd[29776]: Stack mid redzone: f2
bluetoothd[29776]: Stack right redzone: f3
bluetoothd[29776]: Stack after return: f5
bluetoothd[29776]: Stack use after scope: f8
bluetoothd[29776]: Global redzone: f9
bluetoothd[29776]: Global init order: f6
bluetoothd[29776]: Poisoned by user: f7
bluetoothd[29776]: Container overflow: fc
bluetoothd[29776]: Array cookie: ac
bluetoothd[29776]: Intra object redzone: bb
bluetoothd[29776]: ASan internal: fe
bluetoothd[29776]: Left alloca redzone: ca
bluetoothd[29776]: Right alloca redzone: cb
bluetoothd[29776]: ==29776==ABORTING

While removing device. In this case I had paired one of the earbuds
which paired also the other one via CSIP. However, this ended up with
duplicate entries for the same device in bluetoothctl devices:

[Galaxy Buds2 Pro]# devices
Device 28:3D:C2:4A:7D:2A Galaxy Buds2 Pro
Device 28:3D:C2:4A:7E:DA Galaxy Buds2 Pro
Device 28:3D:C2:4A:7D:2A Galaxy Buds2 Pro

When removing (or maybe disconnecting) the duplicate devices bluetoothd crashed.

bluetoothd[65356]: =================================================================
bluetoothd[65356]: ==65356==ERROR: AddressSanitizer: heap-use-after-free on address 0x61700000d618 at pc 0x000000672263 bp 0x7fff4830e>
bluetoothd[65356]: READ of size 8 at 0x61700000d618 thread T0
bluetoothd[65356]: #0 0x672262 in device_get_path src/device.c:5752
bluetoothd[65356]: #1 0x69df24 in append_device src/set.c:109
bluetoothd[65356]: #2 0x7132b1 in queue_foreach src/shared/queue.c:207
bluetoothd[65356]: #3 0x69e10b in get_devices src/set.c:125
bluetoothd[65356]: #4 0x6f9aed in append_property gdbus/object.c:498
bluetoothd[65356]: #5 0x6f9fd3 in append_properties gdbus/object.c:527
bluetoothd[65356]: #6 0x6fa22f in append_interface gdbus/object.c:542
bluetoothd[65356]: #7 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
bluetoothd[65356]: #8 0x6ff44d in append_interfaces gdbus/object.c:1104
bluetoothd[65356]: #9 0x6ff62b in append_object gdbus/object.c:1119
bluetoothd[65356]: #10 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
bluetoothd[65356]: #11 0x6ff6e5 in append_object gdbus/object.c:1122
bluetoothd[65356]: #12 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
bluetoothd[65356]: #13 0x6ff6e5 in append_object gdbus/object.c:1122
bluetoothd[65356]: #14 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
bluetoothd[65356]: #15 0x6ff908 in get_objects gdbus/object.c:1154
bluetoothd[65356]: #16 0x6f7c8a in process_message gdbus/object.c:246
bluetoothd[65356]: #17 0x6ff1c1 in generic_message gdbus/object.c:1072
bluetoothd[65356]: #18 0x7f1c0bfc7d88 in dbus_connection_dispatch (/lib64/libdbus-1.so.3+0x1cd88)
bluetoothd[65356]: #19 0x6ee2f9 in message_dispatch gdbus/mainloop.c:59
bluetoothd[65356]: #20 0x7f1c0c726c71 in g_idle_dispatch (/lib64/libglib-2.0.so.0+0x55c71)
bluetoothd[65356]: #21 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
bluetoothd[65356]: #22 0x7f1c0c77e117 in g_main_context_iterate.constprop.0 (/lib64/libglib-2.0.so.0+0xad117)
bluetoothd[65356]: #23 0x7f1c0c72724e in g_main_loop_run (/lib64/libglib-2.0.so.0+0x5624e)
bluetoothd[65356]: #24 0x7d8fe5 in mainloop_run src/shared/mainloop-glib.c:66
bluetoothd[65356]: #25 0x7d9d0c in mainloop_run_with_signal src/shared/mainloop-notify.c:188
bluetoothd[65356]: #26 0x55497c in main src/main.c:1428
bluetoothd[65356]: #27 0x7f1c0b64a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
bluetoothd[65356]: #28 0x7f1c0b64a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8)
bluetoothd[65356]: #29 0x40da34 in _start (/usr/local/stow/bluez-dev/libexec/bluetooth/bluetoothd+0x40da34)
bluetoothd[65356]: 0x61700000d618 is located 24 bytes inside of 680-byte region [0x61700000d600,0x61700000d8a8)
bluetoothd[65356]: freed by thread T0 here:
bluetoothd[65356]: #0 0x7f1c0c0b9388 in __interceptor_free.part.0 (/lib64/libasan.so.8+0xb9388)
bluetoothd[65356]: #1 0x7f1c0c72c88c in g_free (/lib64/libglib-2.0.so.0+0x5b88c)
bluetoothd[65356]: #2 0x6fb494 in remove_interface gdbus/object.c:660
bluetoothd[65356]: #3 0x70190f in g_dbus_unregister_interface gdbus/object.c:1394
bluetoothd[65356]: #4 0x67b761 in btd_device_unref src/device.c:6883
bluetoothd[65356]: #5 0x666838 in device_remove src/device.c:4527
bluetoothd[65356]: #6 0x5c0e21 in btd_adapter_remove_device src/adapter.c:1441
bluetoothd[65356]: #7 0x5f2d01 in adapter_remove_connection src/adapter.c:7381
bluetoothd[65356]: #8 0x5fc074 in dev_disconnected src/adapter.c:8352
bluetoothd[65356]: #9 0x5fc468 in disconnect_complete src/adapter.c:8392
bluetoothd[65356]: #10 0x71aec3 in request_complete src/shared/mgmt.c:306
bluetoothd[65356]: #11 0x71c271 in can_read_data src/shared/mgmt.c:394
bluetoothd[65356]: #12 0x7d7d25 in watch_callback src/shared/io-glib.c:157
bluetoothd[65356]: #13 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
bluetoothd[65356]: previously allocated by thread T0 here:
bluetoothd[65356]: #0 0x7f1c0c0ba097 in calloc (/lib64/libasan.so.8+0xba097)
bluetoothd[65356]: #1 0x661074 in device_new src/device.c:4006
bluetoothd[65356]: #2 0x661c94 in device_create src/device.c:4097
bluetoothd[65356]: #3 0x5c0182 in adapter_create_device src/adapter.c:1373
bluetoothd[65356]: #4 0x5f0c23 in btd_adapter_update_found_device src/adapter.c:7130
bluetoothd[65356]: #5 0x5f280c in device_found_callback src/adapter.c:7341
bluetoothd[65356]: #6 0x71b53f in notify_handler src/shared/mgmt.c:337
bluetoothd[65356]: #7 0x7132b1 in queue_foreach src/shared/queue.c:207
bluetoothd[65356]: #8 0x71b7f3 in process_notify src/shared/mgmt.c:349
bluetoothd[65356]: #9 0x71c60a in can_read_data src/shared/mgmt.c:409
bluetoothd[65356]: #10 0x7d7d25 in watch_callback src/shared/io-glib.c:157
bluetoothd[65356]: #11 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
bluetoothd[65356]: SUMMARY: AddressSanitizer: heap-use-after-free src/device.c:5752 in device_get_path
bluetoothd[65356]: Shadow bytes around the buggy address:
bluetoothd[65356]: 0x0c2e7fff9a70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9a90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9aa0: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
bluetoothd[65356]: 0x0c2e7fff9ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
bluetoothd[65356]: =>0x0c2e7fff9ac0: fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9ad0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9ae0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9af0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
bluetoothd[65356]: 0x0c2e7fff9b10: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
bluetoothd[65356]: Shadow byte legend (one shadow byte represents 8 application bytes):
bluetoothd[65356]: Addressable: 00
bluetoothd[65356]: Partially addressable: 01 02 03 04 05 06 07
bluetoothd[65356]: Heap left redzone: fa
bluetoothd[65356]: Freed heap region: fd
bluetoothd[65356]: Stack left redzone: f1
bluetoothd[65356]: Stack mid redzone: f2
bluetoothd[65356]: Stack right redzone: f3
bluetoothd[65356]: Stack after return: f5
bluetoothd[65356]: Stack use after scope: f8
bluetoothd[65356]: Global redzone: f9
bluetoothd[65356]: Global init order: f6
bluetoothd[65356]: Poisoned by user: f7
bluetoothd[65356]: Container overflow: fc
bluetoothd[65356]: Array cookie: ac
bluetoothd[65356]: Intra object redzone: bb
bluetoothd[65356]: ASan internal: fe
bluetoothd[65356]: Left alloca redzone: ca
bluetoothd[65356]: Right alloca redzone: cb
bluetoothd[65356]: ==65356==ABORTING

"Rank" is also typoed to "Size" in the code below.

The spec seems to allow the same device to belogn to multiple device
sets. I think this was not allowed in the initial implementation below,
but maybe room for it should be in the DBus API.

> ---
> Makefile.am | 3 +-
> src/adapter.c | 6 +-
> src/device.c | 124 ++++++++++++++++++-
> src/device.h | 11 +-
> src/set.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++
> src/set.h | 14 +++
> 6 files changed, 482 insertions(+), 11 deletions(-)
> create mode 100644 src/set.c
> create mode 100644 src/set.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 0e8cce249c7d..7b010c8159e9 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -326,7 +326,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
> src/eir.h src/eir.c \
> src/adv_monitor.h src/adv_monitor.c \
> src/battery.h src/battery.c \
> - src/settings.h src/settings.c
> + src/settings.h src/settings.c \
> + src/set.h src/set.c
> src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
> gdbus/libgdbus-internal.la \
> src/libshared-glib.la \
> diff --git a/src/adapter.c b/src/adapter.c
> index 538310c1ddc6..ae0eb364bad5 100644
> --- a/src/adapter.c
> +++ b/src/adapter.c
> @@ -4387,8 +4387,8 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys)
> if (dev) {
> device_set_paired(dev, info->bdaddr_type);
> device_set_bonded(dev, info->bdaddr_type);
> - device_set_ltk_enc_size(dev, info->enc_size);
> - device_set_ltk_enc_size(dev, info->enc_size);
> + device_set_ltk(dev, info->val, info->central,
> + info->enc_size);
> }
> }
>
> @@ -8657,7 +8657,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
> device_set_bonded(device, addr->type);
> }
>
> - device_set_ltk_enc_size(device, ev->key.enc_size);
> + device_set_ltk(device, ev->key.val, ev->key.central, ev->key.enc_size);
>
> bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
> }
> diff --git a/src/device.c b/src/device.c
> index cb16d37c1ae1..caee2183b9d8 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -64,6 +64,7 @@
> #include "storage.h"
> #include "eir.h"
> #include "settings.h"
> +#include "set.h"
>
> #define DISCONNECT_TIMER 2
> #define DISCOVERY_TIMER 1
> @@ -159,11 +160,24 @@ struct bearer_state {
> time_t last_seen;
> };
>
> +struct ltk_info {
> + uint8_t key[16];
> + bool central;
> + uint8_t enc_size;
> +};
> +
> struct csrk_info {
> uint8_t key[16];
> uint32_t counter;
> };
>
> +struct sirk_info {
> + uint8_t encrypted;
> + uint8_t key[16];
> + uint8_t size;
> + uint8_t rank;
> +};
> +
> enum {
> WAKE_FLAG_DEFAULT = 0,
> WAKE_FLAG_ENABLED,
> @@ -253,7 +267,8 @@ struct btd_device {
>
> struct csrk_info *local_csrk;
> struct csrk_info *remote_csrk;
> - uint8_t ltk_enc_size;
> + struct ltk_info *ltk;
> + struct sirk_info *sirk;
>
> sdp_list_t *tmp_records;
>
> @@ -386,6 +401,21 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
> g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
> }
>
> +static void store_sirk(struct sirk_info *sirk, GKeyFile *key_file)
> +{
> + const char *group = "SetIdentityResolvingKey";
> + char key[33];
> + int i;
> +
> + for (i = 0; i < 16; i++)
> + sprintf(key + (i * 2), "%2.2X", sirk->key[i]);
> +
> + g_key_file_set_boolean(key_file, group, "Encrypted", sirk->encrypted);
> + g_key_file_set_string(key_file, group, "Key", key);
> + g_key_file_set_integer(key_file, group, "Size", sirk->size);
> + g_key_file_set_integer(key_file, group, "Size", sirk->rank);

This should be "Rank", not "Size".

> +}
> +
> static gboolean store_device_info_cb(gpointer user_data)
> {
> struct btd_device *device = user_data;
> @@ -483,6 +513,9 @@ static gboolean store_device_info_cb(gpointer user_data)
> if (device->remote_csrk)
> store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey");
>
> + if (device->sirk)
> + store_sirk(device->sirk, key_file);
> +
> str = g_key_file_to_data(key_file, &length, NULL);
> if (!g_file_set_contents(filename, str, length, &gerr)) {
> error("Unable set contents for %s: (%s)", filename,
> @@ -806,6 +839,7 @@ static void device_free(gpointer user_data)
>
> g_free(device->local_csrk);
> g_free(device->remote_csrk);
> + free(device->ltk);
> g_free(device->path);
> g_free(device->alias);
> free(device->modalias);
> @@ -1792,10 +1826,43 @@ bool device_is_disconnecting(struct btd_device *device)
> return device->disconn_timer > 0;
> }
>
> -void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size)
> +void device_set_ltk(struct btd_device *device, const uint8_t val[16],
> + bool central, uint8_t enc_size)
> {
> - device->ltk_enc_size = enc_size;
> - bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
> + if (!device->ltk)
> + device->ltk = new0(struct ltk_info, 1);
> +
> + memcpy(device->ltk->key, val, sizeof(device->ltk->key));
> + device->ltk->central = central;
> + device->ltk->enc_size = enc_size;
> + bt_att_set_enc_key_size(device->att, enc_size);
> +
> + if (device->sirk && device->sirk->encrypted)
> + btd_set_add_device(device, device->ltk->key, device->sirk->key,
> + device->sirk->size,
> + device->sirk->rank);
> +}
> +
> +bool btd_device_add_set(struct btd_device *device, bool encrypted,
> + uint8_t sirk[16], uint8_t size, uint8_t rank)
> +{
> + if (encrypted && !device->ltk)
> + return false;
> +
> + if (!device->sirk)
> + device->sirk = new0(struct sirk_info, 1);
> +
> + device->sirk->encrypted = encrypted;
> + memcpy(device->sirk->key, sirk, sizeof(device->sirk->key));
> + device->sirk->size = size;
> + device->sirk->rank = rank;
> +
> + btd_set_add_device(device, encrypted ? device->ltk->key : NULL, sirk,
> + size, rank);
> +
> + store_device_info(device);
> +
> + return true;
> }
>
> static void device_set_auto_connect(struct btd_device *device, gboolean enable)
> @@ -3290,6 +3357,39 @@ fail:
> return NULL;
> }
>
> +static struct sirk_info *load_sirk(GKeyFile *key_file)
> +{
> + const char *group = "SetIdentityResolvingKey";
> + struct sirk_info *sirk;
> + char *str;
> + int i;
> +
> + str = g_key_file_get_string(key_file, group, "Key", NULL);
> + if (!str)
> + return NULL;
> +
> + sirk = g_new0(struct sirk_info, 1);
> +
> + for (i = 0; i < 16; i++) {
> + if (sscanf(str + (i * 2), "%2hhx", &sirk->key[i]) != 1)
> + goto fail;
> + }
> +
> +
> + sirk->encrypted = g_key_file_get_boolean(key_file, group, "Encrypted",
> + NULL);
> + sirk->size = g_key_file_get_integer(key_file, group, "Size", NULL);
> + sirk->rank = g_key_file_get_integer(key_file, group, "Rank", NULL);
> + g_free(str);
> +
> + return sirk;
> +
> +fail:
> + g_free(str);
> + g_free(sirk);
> + return NULL;
> +}
> +
> static void load_services(struct btd_device *device, char **uuids)
> {
> char **uuid;
> @@ -3430,6 +3530,12 @@ static void load_info(struct btd_device *device, const char *local,
>
> device->local_csrk = load_csrk(key_file, "LocalSignatureKey");
> device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey");
> +
> + device->sirk = load_sirk(key_file);
> + if (device->sirk && !device->sirk->encrypted)
> + btd_set_add_device(device, NULL, device->sirk->key,
> + device->sirk->size,
> + device->sirk->rank);
> }
>
> g_strfreev(techno);
> @@ -5207,7 +5313,9 @@ static void gatt_server_init(struct btd_device *device,
> return;
> }
>
> - bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
> + if (device->ltk)
> + bt_att_set_enc_key_size(device->att, device->ltk->enc_size);
> +
> bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
>
> btd_gatt_database_server_connected(database, device->server);
> @@ -6928,3 +7036,9 @@ int8_t btd_device_get_volume(struct btd_device *device)
> {
> return device->volume;
> }
> +
> +void btd_device_foreach_ad(struct btd_device *dev, bt_ad_func_t func,
> + void *data)
> +{
> + bt_ad_foreach_data(dev->ad, func, data);
> +}
> diff --git a/src/device.h b/src/device.h
> index 9e81fda9e948..96347ff229cc 100644
> --- a/src/device.h
> +++ b/src/device.h
> @@ -128,8 +128,10 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
> bool *remove);
> void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
> bool device_is_disconnecting(struct btd_device *device);
> -void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size);
> -
> +void device_set_ltk(struct btd_device *device, const uint8_t val[16],
> + bool central, uint8_t enc_size);
> +bool btd_device_add_set(struct btd_device *device, bool encrypted,
> + uint8_t sirk[16], uint8_t size, uint8_t rank);
> void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type,
> uint16_t value);
> void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le,
> @@ -188,3 +190,8 @@ void btd_device_cleanup(void);
>
> void btd_device_set_volume(struct btd_device *dev, int8_t volume);
> int8_t btd_device_get_volume(struct btd_device *dev);
> +
> +typedef void (*bt_device_ad_func_t)(void *data, void *user_data);
> +
> +void btd_device_foreach_ad(struct btd_device *dev, bt_device_ad_func_t func,
> + void *data);
> diff --git a/src/set.c b/src/set.c
> new file mode 100644
> index 000000000000..6b3cd82651e6
> --- /dev/null
> +++ b/src/set.c
> @@ -0,0 +1,335 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2023 Intel Corporation
> + *
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <errno.h>
> +
> +#include <glib.h>
> +#include <dbus/dbus.h>
> +
> +#include "gdbus/gdbus.h"
> +#include "src/shared/util.h"
> +#include "src/shared/queue.h"
> +#include "src/shared/ad.h"
> +#include "src/shared/crypto.h"
> +
> +#include "log.h"
> +#include "error.h"
> +#include "adapter.h"
> +#include "device.h"
> +#include "dbus-common.h"
> +#include "set.h"
> +
> +static struct queue *set_list;
> +
> +struct btd_device_set {
> + struct btd_adapter *adapter;
> + char *path;
> + uint8_t sirk[16];
> + uint8_t size;
> + uint8_t rank;
> + bool auto_connect;
> + struct queue *devices;
> + struct btd_device *device;
> +};
> +
> +static DBusMessage *set_disconnect(DBusConnection *conn, DBusMessage *msg,
> + void *user_data)
> +{
> + /* TODO */
> + return NULL;
> +}
> +
> +static DBusMessage *set_connect(DBusConnection *conn, DBusMessage *msg,
> + void *user_data)
> +{
> + /* TODO */
> + return NULL;
> +}
> +
> +static const GDBusMethodTable set_methods[] = {
> + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Disconnect", NULL, NULL,
> + set_disconnect) },
> + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Connect", NULL, NULL,
> + set_connect) },
> + {}
> +};
> +
> +static gboolean get_auto_connect(const GDBusPropertyTable *property,
> + DBusMessageIter *iter, void *data)
> +{
> + struct btd_device_set *set = data;
> +
> + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
> + &set->auto_connect);
> +
> + return TRUE;
> +}
> +
> +static void set_auto_connect(const GDBusPropertyTable *property,
> + DBusMessageIter *iter,
> + GDBusPendingPropertySet id, void *data)
> +{
> + struct btd_device_set *set = data;
> + dbus_bool_t b;
> +
> + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
> + g_dbus_pending_property_error(id,
> + ERROR_INTERFACE ".InvalidArguments",
> + "Invalid arguments in method call");
> + return;
> + }
> +
> + dbus_message_iter_get_basic(iter, &b);
> +
> + set->auto_connect = b ? true : false;
> +
> + g_dbus_pending_property_success(id);
> +}
> +
> +static void append_device(void *data, void *user_data)
> +{
> + struct btd_device *device = data;
> + const char *path = device_get_path(device);
> + DBusMessageIter *entry = user_data;
> +
> + dbus_message_iter_append_basic(entry, DBUS_TYPE_OBJECT_PATH, &path);
> +}
> +
> +static gboolean get_devices(const GDBusPropertyTable *property,
> + DBusMessageIter *iter, void *data)
> +{
> + struct btd_device_set *set = data;
> + DBusMessageIter entry;
> +
> + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
> + DBUS_TYPE_OBJECT_PATH_AS_STRING,
> + &entry);
> +
> + queue_foreach(set->devices, append_device, &entry);
> +
> + dbus_message_iter_close_container(iter, &entry);
> +
> + return TRUE;
> +}
> +
> +static gboolean get_size(const GDBusPropertyTable *property,
> + DBusMessageIter *iter, void *data)
> +{
> + struct btd_device_set *set = data;
> +
> + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->size);
> +
> + return TRUE;
> +}
> +
> +static gboolean get_rank(const GDBusPropertyTable *property,
> + DBusMessageIter *iter, void *data)
> +{
> + struct btd_device_set *set = data;
> +
> + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->rank);
> +
> + return TRUE;
> +}
> +
> +static const GDBusPropertyTable set_properties[] = {
> + { "AutoConnect", "b", get_auto_connect, set_auto_connect, NULL,
> + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> + { "Devices", "ao", get_devices, NULL, NULL,
> + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> + { "Size", "y", get_size, NULL, NULL,
> + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> + { "Rank", "y", get_rank, NULL, NULL,
> + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> + {}
> +};
> +
> +static void set_free(void *data)
> +{
> + struct btd_device_set *set = data;
> +
> + queue_destroy(set->devices, NULL);
> + free(set);
> +}
> +
> +static struct btd_device_set *set_new(struct btd_device *device,
> + uint8_t sirk[16], uint8_t size,
> + uint8_t rank)
> +{
> + struct btd_device_set *set;
> +
> + set = new0(struct btd_device_set, 1);
> + set->adapter = device_get_adapter(device);
> + memcpy(set->sirk, sirk, sizeof(set->sirk));
> + set->size = size;
> + set->rank = rank;
> + set->auto_connect = true;
> + set->devices = queue_new();
> + queue_push_tail(set->devices, device);
> + set->path = g_strdup_printf("%s/set_%02x%02x%02x%02x%02x%02x%02x%02x"
> + "%02x%02x%02x%02x%02x%02x%02x%02x",
> + adapter_get_path(set->adapter),
> + sirk[15], sirk[14], sirk[13], sirk[12],
> + sirk[11], sirk[10], sirk[9], sirk[8],
> + sirk[7], sirk[6], sirk[5], sirk[4],
> + sirk[3], sirk[2], sirk[1], sirk[0]);
> +
> + DBG("Creating set %s", set->path);
> +
> + if (g_dbus_register_interface(btd_get_dbus_connection(),
> + set->path, BTD_DEVICE_SET_INTERFACE,
> + set_methods, NULL,
> + set_properties, set,
> + set_free) == FALSE) {
> + error("Unable to register set interface");
> + set_free(set);
> + return NULL;
> + }
> +
> + return set;
> +}
> +
> +static struct btd_device_set *set_find(struct btd_device *device,
> + uint8_t sirk[16])
> +{
> + struct btd_adapter *adapter = device_get_adapter(device);
> + const struct queue_entry *entry;
> +
> + for (entry = queue_get_entries(set_list); entry; entry = entry->next) {
> + struct btd_device_set *set = entry->data;
> +
> + if (set->adapter != adapter)
> + continue;
> +
> + if (!memcmp(set->sirk, sirk, sizeof(set->sirk)))
> + return set;
> + }
> +
> + return NULL;
> +}
> +
> +static void set_connect_next(struct btd_device_set *set)
> +{
> + const struct queue_entry *entry;
> +
> + for (entry = queue_get_entries(set->devices); entry;
> + entry = entry->next) {
> + struct btd_device *device = entry->data;
> +
> + /* Only connect one at time(?) */
> + if (!device_connect_le(device))
> + return;
> + }
> +}
> +
> +static void set_add(struct btd_device_set *set, struct btd_device *device)
> +{
> + /* Check if device is already part of the set then skip to connect */
> + if (queue_find(set->devices, NULL, device))
> + goto done;
> +
> + DBG("set %s device %s", set->path, device_get_path(device));
> +
> + queue_push_tail(set->devices, device);
> + g_dbus_emit_property_changed(btd_get_dbus_connection(), set->path,
> + BTD_DEVICE_SET_INTERFACE, "Devices");
> +
> +done:
> + /* Check if set is marked to auto-connect */
> + if (btd_device_is_connected(device) && set->auto_connect)
> + set_connect_next(set);
> +}
> +
> +static void foreach_rsi(void *data, void *user_data)
> +{
> + struct bt_ad_data *ad = data;
> + struct btd_device_set *set = user_data;
> + struct bt_crypto *crypto;
> + uint8_t res[3];
> +
> + if (ad->type != BT_AD_CSIP_RSI || ad->len < 6)
> + return;
> +
> + crypto = bt_crypto_new();
> + if (!crypto)
> + return;
> +
> + if (!bt_crypto_sih(crypto, set->sirk, ad->data + 3, res)) {
> + bt_crypto_unref(crypto);
> + return;
> + }
> +
> + bt_crypto_unref(crypto);
> +
> + if (!memcmp(ad->data, res, sizeof(res)))
> + device_connect_le(set->device);
> +}
> +
> +static void foreach_device(struct btd_device *device, void *data)
> +{
> + struct btd_device_set *set = data;
> +
> + /* Check if device is already part of the set then skip */
> + if (queue_find(set->devices, NULL, device))
> + return;
> +
> + set->device = device;
> +
> + btd_device_foreach_ad(device, foreach_rsi, set);
> +}
> +
> +void btd_set_add_device(struct btd_device *device, uint8_t *key,
> + uint8_t sirk[16], uint8_t size, uint8_t rank)
> +{
> + struct btd_device_set *set;
> +
> + /* In case key has been set it means SIRK is encrypted */
> + if (key) {
> + struct bt_crypto *crypto = bt_crypto_new();
> +
> + if (!crypto)
> + return;
> +
> + /* sef and sdf are symmetric */
> + bt_crypto_sef(crypto, key, sirk, sirk);
> +
> + bt_crypto_unref(crypto);
> + }
> +
> + /* Check if DeviceSet already exists */
> + set = set_find(device, sirk);
> + if (set) {
> + set_add(set, device);
> + return;
> + }
> +
> + set = set_new(device, sirk, size, rank);
> + if (!set)
> + return;
> +
> + if (!set_list)
> + set_list = queue_new();
> +
> + queue_push_tail(set_list, set);
> +
> + /* Attempt to add devices which have matching RSI */
> + btd_adapter_for_each_device(device_get_adapter(device), foreach_device,
> + set);
> +}
> diff --git a/src/set.h b/src/set.h
> new file mode 100644
> index 000000000000..c3831ef9ca66
> --- /dev/null
> +++ b/src/set.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2023 Intel Corporation
> + *
> + *
> + */
> +
> +#define BTD_DEVICE_SET_INTERFACE "org.bluez.DeviceSet1"
> +
> +void btd_set_add_device(struct btd_device *device, uint8_t *ltk,
> + uint8_t sirk[16], uint8_t size, uint8_t rank);

--
Pauli Virtanen

2023-03-05 22:27:33

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC 4/9] core: Add initial implementation of DeviceSet interface

Hi Pauli,

On Sun, Mar 5, 2023 at 1:57 PM Pauli Virtanen <[email protected]> wrote:
>
> Hi,
>
> to, 2023-03-02 kello 17:27 -0800, Luiz Augusto von Dentz kirjoitti:
> > From: Luiz Augusto von Dentz <[email protected]>
> >
> > This adds the initial implementation of DeviceSet interface as
> > documented in doc/set-api.rst.
>
> Tried this with Samsung Galaxy Buds2 Pro, things mostly seems to work.
> When pairing, it asks to pair also the other earbud, and when
> connecting one it usually also connects the other one.
>
> On DBus, the deviceset appears correctly.
>
> I think on some of the attempts the pairing/connecting of the other set
> device did not work properly, but I don't have good information on this
> right now and nothing reproducible, will need to test again so just
> quick impressions now.
>
> (Right now I'm not able to play audio to both earbuds at the same time.
> This is unrelated to this patchset, and probably known issue similar to
> multiple CIS to same device. What seems to be happening is the same
> that there is a successful "LE Set Connected Isochronous Group
> Parameters" creating two connection handles but no following "LE Create
> Connected Isochronous Stream" so connect timeouts.)
>
> On one attempt, the other device auto-paired, bluetoothctl asked
> "Accept pairing" and the device appeared in "bluetoothctl devices".
> Trying to connect to it in bluetoothctl after that caused it to not
> connect successfully (just "Attempting to connect to ...") but reason
> was unclear.
>
> The device uses the random address scheme where the address changes on
> pairing. When I connected the device the first time, I see
>
> [Galaxy Buds2 Pro]# endpoint.list
> Endpoint /org/bluez/hci1/dev_69_1E_A2_F9_2D_6B/pac_source0
> Endpoint /org/bluez/hci1/dev_69_1E_A2_F9_2D_6B/pac_sink0
> Endpoint /org/bluez/hci1/dev_63_BD_D1_39_E8_75/pac_source0
> Endpoint /org/bluez/hci1/dev_63_BD_D1_39_E8_75/pac_sink0

We cant change the path since that object is how we do pairing and
other stuff and it gets exposed to plugins, so it is normal that it
would stay using the random address until you restart the daemon.

> which shows the random addresses. Sometimes the device and endpoint
> names get the resolved address, but this does not alway happen.
> Sometimes only of the devices gets the resolved in the names.
>
> Sometimes after pairing, in bluetoothctl I see three devices, one of
> them the old random address of one of the two other devices. Sometimes
> it's three devices with one of the addresses listed twice.

Yeah, I seem this as well, I think it might be a bug in bluetoothctl though.

> A few ASAN crashes:
>
> While connecting device:
>
> bluetoothd[29776]: =================================================================
> bluetoothd[29776]: ==29776==ERROR: AddressSanitizer: heap-use-after-free on address 0x6170000001f0 at pc 0x000000670565 bp 0x7ffca7750f50 sp 0x7ffca7750f48
> bluetoothd[29776]: READ of size 8 at 0x6170000001f0 thread T0
> bluetoothd[29776]: #0 0x670564 in device_connect_le src/device.c:5526
> bluetoothd[29776]: #1 0x69f457 in set_connect_next src/set.c:237
> bluetoothd[29776]: #2 0x69f7d9 in set_add src/set.c:257
> bluetoothd[29776]: #3 0x69fdcf in btd_set_add_device src/set.c:319
> bluetoothd[29776]: #4 0x64dcc0 in btd_device_add_set src/device.c:1860
> bluetoothd[29776]: #5 0x521c2f in csip_ready profiles/audio/csip.c:264
> bluetoothd[29776]: #6 0x7d389e in csip_notify_ready src/shared/csip.c:578
> bluetoothd[29776]: #7 0x7d43b6 in csip_idle src/shared/csip.c:659
> bluetoothd[29776]: #8 0x7437d1 in idle_notify src/shared/gatt-client.c:171
> bluetoothd[29776]: #9 0x713c7b in queue_remove_if src/shared/queue.c:279
> bluetoothd[29776]: #10 0x7142af in queue_remove_all src/shared/queue.c:321
> bluetoothd[29776]: #11 0x74388b in notify_client_idle src/shared/gatt-client.c:180
> bluetoothd[29776]: #12 0x743cb9 in request_unref src/shared/gatt-client.c:199
> bluetoothd[29776]: #13 0x732bb1 in destroy_att_send_op src/shared/att.c:209
> bluetoothd[29776]: #14 0x739773 in handle_rsp src/shared/att.c:862
> bluetoothd[29776]: #15 0x73ae8f in can_read_data src/shared/att.c:1052
> bluetoothd[29776]: #16 0x7d7d25 in watch_callback src/shared/io-glib.c:157
> bluetoothd[29776]: #17 0x7ff21f9a6c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
> bluetoothd[29776]: #18 0x7ff21f9fd117 in g_main_context_iterate.constprop.0 (/lib64/libglib-2.0.so.0+0xad117)
> bluetoothd[29776]: #19 0x7ff21f9a624e in g_main_loop_run (/lib64/libglib-2.0.so.0+0x5624e)
> bluetoothd[29776]: #20 0x7d8fe5 in mainloop_run src/shared/mainloop-glib.c:66
> bluetoothd[29776]: #21 0x7d9d0c in mainloop_run_with_signal src/shared/mainloop-notify.c:188
> bluetoothd[29776]: #22 0x55497c in main src/main.c:1428
> bluetoothd[29776]: #23 0x7ff21e84a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
> bluetoothd[29776]: #24 0x7ff21e84a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8)
> bluetoothd[29776]: #25 0x40da34 in _start (/usr/local/stow/bluez-dev/libexec/bluetooth/bluetoothd+0x40da34)
> bluetoothd[29776]: 0x6170000001f0 is located 368 bytes inside of 680-byte region [0x617000000080,0x617000000328)
> bluetoothd[29776]: freed by thread T0 here:
> bluetoothd[29776]: #0 0x7ff21f2b9388 in __interceptor_free.part.0 (/lib64/libasan.so.8+0xb9388)
> bluetoothd[29776]: #1 0x7ff21f9ab88c in g_free (/lib64/libglib-2.0.so.0+0x5b88c)
> bluetoothd[29776]: #2 0x6fb494 in remove_interface gdbus/object.c:660
> bluetoothd[29776]: #3 0x70190f in g_dbus_unregister_interface gdbus/object.c:1394
> bluetoothd[29776]: #4 0x67b761 in btd_device_unref src/device.c:6883
> bluetoothd[29776]: #5 0x666838 in device_remove src/device.c:4527
> bluetoothd[29776]: #6 0x5c0e21 in btd_adapter_remove_device src/adapter.c:1441
> bluetoothd[29776]: #7 0x5d269d in remove_device src/adapter.c:3546
> bluetoothd[29776]: #8 0x6f7c8a in process_message gdbus/object.c:246
> bluetoothd[29776]: #9 0x6ff1c1 in generic_message gdbus/object.c:1072
> bluetoothd[29776]: #10 0x7ff21f917d88 in dbus_connection_dispatch (/lib64/libdbus-1.so.3+0x1cd88)
> bluetoothd[29776]: previously allocated by thread T0 here:
> bluetoothd[29776]: #0 0x7ff21f2ba097 in calloc (/lib64/libasan.so.8+0xba097)
> bluetoothd[29776]: #1 0x661074 in device_new src/device.c:4006
> bluetoothd[29776]: #2 0x661b34 in device_create_from_storage src/device.c:4072
> bluetoothd[29776]: #3 0x5de389 in load_devices src/adapter.c:4930
> bluetoothd[29776]: #4 0x6051fe in adapter_register src/adapter.c:9264
> bluetoothd[29776]: #5 0x60da49 in read_info_complete src/adapter.c:10159
> bluetoothd[29776]: #6 0x71aec3 in request_complete src/shared/mgmt.c:306
> bluetoothd[29776]: #7 0x71c271 in can_read_data src/shared/mgmt.c:394
> bluetoothd[29776]: #8 0x7d7d25 in watch_callback src/shared/io-glib.c:157
> bluetoothd[29776]: #9 0x7ff21f9a6c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
> bluetoothd[29776]: SUMMARY: AddressSanitizer: heap-use-after-free src/device.c:5526 in device_connect_le
> bluetoothd[29776]: Shadow bytes around the buggy address:
> bluetoothd[29776]: 0x0c2e7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> bluetoothd[29776]: 0x0c2e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> bluetoothd[29776]: 0x0c2e7fff8000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> bluetoothd[29776]: 0x0c2e7fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[29776]: 0x0c2e7fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[29776]: =>0x0c2e7fff8030: fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]fd
> bluetoothd[29776]: 0x0c2e7fff8040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[29776]: 0x0c2e7fff8050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[29776]: 0x0c2e7fff8060: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
> bluetoothd[29776]: 0x0c2e7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> bluetoothd[29776]: 0x0c2e7fff8080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> bluetoothd[29776]: Shadow byte legend (one shadow byte represents 8 application bytes):
> bluetoothd[29776]: Addressable: 00
> bluetoothd[29776]: Partially addressable: 01 02 03 04 05 06 07
> bluetoothd[29776]: Heap left redzone: fa
> bluetoothd[29776]: Freed heap region: fd
> bluetoothd[29776]: Stack left redzone: f1
> bluetoothd[29776]: Stack mid redzone: f2
> bluetoothd[29776]: Stack right redzone: f3
> bluetoothd[29776]: Stack after return: f5
> bluetoothd[29776]: Stack use after scope: f8
> bluetoothd[29776]: Global redzone: f9
> bluetoothd[29776]: Global init order: f6
> bluetoothd[29776]: Poisoned by user: f7
> bluetoothd[29776]: Container overflow: fc
> bluetoothd[29776]: Array cookie: ac
> bluetoothd[29776]: Intra object redzone: bb
> bluetoothd[29776]: ASan internal: fe
> bluetoothd[29776]: Left alloca redzone: ca
> bluetoothd[29776]: Right alloca redzone: cb
> bluetoothd[29776]: ==29776==ABORTING
>
> While removing device. In this case I had paired one of the earbuds
> which paired also the other one via CSIP. However, this ended up with
> duplicate entries for the same device in bluetoothctl devices:
>
> [Galaxy Buds2 Pro]# devices
> Device 28:3D:C2:4A:7D:2A Galaxy Buds2 Pro
> Device 28:3D:C2:4A:7E:DA Galaxy Buds2 Pro
> Device 28:3D:C2:4A:7D:2A Galaxy Buds2 Pro
>
> When removing (or maybe disconnecting) the duplicate devices bluetoothd crashed.
>
> bluetoothd[65356]: =================================================================
> bluetoothd[65356]: ==65356==ERROR: AddressSanitizer: heap-use-after-free on address 0x61700000d618 at pc 0x000000672263 bp 0x7fff4830e>
> bluetoothd[65356]: READ of size 8 at 0x61700000d618 thread T0
> bluetoothd[65356]: #0 0x672262 in device_get_path src/device.c:5752
> bluetoothd[65356]: #1 0x69df24 in append_device src/set.c:109
> bluetoothd[65356]: #2 0x7132b1 in queue_foreach src/shared/queue.c:207
> bluetoothd[65356]: #3 0x69e10b in get_devices src/set.c:125
> bluetoothd[65356]: #4 0x6f9aed in append_property gdbus/object.c:498
> bluetoothd[65356]: #5 0x6f9fd3 in append_properties gdbus/object.c:527
> bluetoothd[65356]: #6 0x6fa22f in append_interface gdbus/object.c:542
> bluetoothd[65356]: #7 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
> bluetoothd[65356]: #8 0x6ff44d in append_interfaces gdbus/object.c:1104
> bluetoothd[65356]: #9 0x6ff62b in append_object gdbus/object.c:1119
> bluetoothd[65356]: #10 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
> bluetoothd[65356]: #11 0x6ff6e5 in append_object gdbus/object.c:1122
> bluetoothd[65356]: #12 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
> bluetoothd[65356]: #13 0x6ff6e5 in append_object gdbus/object.c:1122
> bluetoothd[65356]: #14 0x7f1c0c743cef in g_slist_foreach (/lib64/libglib-2.0.so.0+0x72cef)
> bluetoothd[65356]: #15 0x6ff908 in get_objects gdbus/object.c:1154
> bluetoothd[65356]: #16 0x6f7c8a in process_message gdbus/object.c:246
> bluetoothd[65356]: #17 0x6ff1c1 in generic_message gdbus/object.c:1072
> bluetoothd[65356]: #18 0x7f1c0bfc7d88 in dbus_connection_dispatch (/lib64/libdbus-1.so.3+0x1cd88)
> bluetoothd[65356]: #19 0x6ee2f9 in message_dispatch gdbus/mainloop.c:59
> bluetoothd[65356]: #20 0x7f1c0c726c71 in g_idle_dispatch (/lib64/libglib-2.0.so.0+0x55c71)
> bluetoothd[65356]: #21 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
> bluetoothd[65356]: #22 0x7f1c0c77e117 in g_main_context_iterate.constprop.0 (/lib64/libglib-2.0.so.0+0xad117)
> bluetoothd[65356]: #23 0x7f1c0c72724e in g_main_loop_run (/lib64/libglib-2.0.so.0+0x5624e)
> bluetoothd[65356]: #24 0x7d8fe5 in mainloop_run src/shared/mainloop-glib.c:66
> bluetoothd[65356]: #25 0x7d9d0c in mainloop_run_with_signal src/shared/mainloop-notify.c:188
> bluetoothd[65356]: #26 0x55497c in main src/main.c:1428
> bluetoothd[65356]: #27 0x7f1c0b64a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
> bluetoothd[65356]: #28 0x7f1c0b64a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8)
> bluetoothd[65356]: #29 0x40da34 in _start (/usr/local/stow/bluez-dev/libexec/bluetooth/bluetoothd+0x40da34)
> bluetoothd[65356]: 0x61700000d618 is located 24 bytes inside of 680-byte region [0x61700000d600,0x61700000d8a8)
> bluetoothd[65356]: freed by thread T0 here:
> bluetoothd[65356]: #0 0x7f1c0c0b9388 in __interceptor_free.part.0 (/lib64/libasan.so.8+0xb9388)
> bluetoothd[65356]: #1 0x7f1c0c72c88c in g_free (/lib64/libglib-2.0.so.0+0x5b88c)
> bluetoothd[65356]: #2 0x6fb494 in remove_interface gdbus/object.c:660
> bluetoothd[65356]: #3 0x70190f in g_dbus_unregister_interface gdbus/object.c:1394
> bluetoothd[65356]: #4 0x67b761 in btd_device_unref src/device.c:6883
> bluetoothd[65356]: #5 0x666838 in device_remove src/device.c:4527
> bluetoothd[65356]: #6 0x5c0e21 in btd_adapter_remove_device src/adapter.c:1441
> bluetoothd[65356]: #7 0x5f2d01 in adapter_remove_connection src/adapter.c:7381
> bluetoothd[65356]: #8 0x5fc074 in dev_disconnected src/adapter.c:8352
> bluetoothd[65356]: #9 0x5fc468 in disconnect_complete src/adapter.c:8392
> bluetoothd[65356]: #10 0x71aec3 in request_complete src/shared/mgmt.c:306
> bluetoothd[65356]: #11 0x71c271 in can_read_data src/shared/mgmt.c:394
> bluetoothd[65356]: #12 0x7d7d25 in watch_callback src/shared/io-glib.c:157
> bluetoothd[65356]: #13 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
> bluetoothd[65356]: previously allocated by thread T0 here:
> bluetoothd[65356]: #0 0x7f1c0c0ba097 in calloc (/lib64/libasan.so.8+0xba097)
> bluetoothd[65356]: #1 0x661074 in device_new src/device.c:4006
> bluetoothd[65356]: #2 0x661c94 in device_create src/device.c:4097
> bluetoothd[65356]: #3 0x5c0182 in adapter_create_device src/adapter.c:1373
> bluetoothd[65356]: #4 0x5f0c23 in btd_adapter_update_found_device src/adapter.c:7130
> bluetoothd[65356]: #5 0x5f280c in device_found_callback src/adapter.c:7341
> bluetoothd[65356]: #6 0x71b53f in notify_handler src/shared/mgmt.c:337
> bluetoothd[65356]: #7 0x7132b1 in queue_foreach src/shared/queue.c:207
> bluetoothd[65356]: #8 0x71b7f3 in process_notify src/shared/mgmt.c:349
> bluetoothd[65356]: #9 0x71c60a in can_read_data src/shared/mgmt.c:409
> bluetoothd[65356]: #10 0x7d7d25 in watch_callback src/shared/io-glib.c:157
> bluetoothd[65356]: #11 0x7f1c0c727c7e in g_main_context_dispatch (/lib64/libglib-2.0.so.0+0x56c7e)
> bluetoothd[65356]: SUMMARY: AddressSanitizer: heap-use-after-free src/device.c:5752 in device_get_path
> bluetoothd[65356]: Shadow bytes around the buggy address:
> bluetoothd[65356]: 0x0c2e7fff9a70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9a90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9aa0: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
> bluetoothd[65356]: 0x0c2e7fff9ab0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
> bluetoothd[65356]: =>0x0c2e7fff9ac0: fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9ad0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9ae0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9af0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
> bluetoothd[65356]: 0x0c2e7fff9b10: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa
> bluetoothd[65356]: Shadow byte legend (one shadow byte represents 8 application bytes):
> bluetoothd[65356]: Addressable: 00
> bluetoothd[65356]: Partially addressable: 01 02 03 04 05 06 07
> bluetoothd[65356]: Heap left redzone: fa
> bluetoothd[65356]: Freed heap region: fd
> bluetoothd[65356]: Stack left redzone: f1
> bluetoothd[65356]: Stack mid redzone: f2
> bluetoothd[65356]: Stack right redzone: f3
> bluetoothd[65356]: Stack after return: f5
> bluetoothd[65356]: Stack use after scope: f8
> bluetoothd[65356]: Global redzone: f9
> bluetoothd[65356]: Global init order: f6
> bluetoothd[65356]: Poisoned by user: f7
> bluetoothd[65356]: Container overflow: fc
> bluetoothd[65356]: Array cookie: ac
> bluetoothd[65356]: Intra object redzone: bb
> bluetoothd[65356]: ASan internal: fe
> bluetoothd[65356]: Left alloca redzone: ca
> bluetoothd[65356]: Right alloca redzone: cb
> bluetoothd[65356]: ==65356==ABORTING
>
> "Rank" is also typoed to "Size" in the code below.
>
> The spec seems to allow the same device to belogn to multiple device
> sets. I think this was not allowed in the initial implementation below,
> but maybe room for it should be in the DBus API.

Well I guess each set would need its one unique SIRK as well, although
it might be a good idea to have the Device.Set property be a list to
allow it to be a member of multiple Sets or have it as something like
a dictionary e.g. a{ov} where o is the DeviceSet and v is another
dictionary which can then be something like: s:"Rank", byte:1, the
dictionary allows use to introduce new properties specific for the
device/set so we don't have to break the API in the future. I will try
to fix the crashes, I haven't tried much of removing the devices
belonging to a set and Im yet to implement proper cleanup if all
devices in a set is removed, or if a new device is found with a
matching RSI while scanning it should be automativally added, and
connected if auto-connect is set.

> > ---
> > Makefile.am | 3 +-
> > src/adapter.c | 6 +-
> > src/device.c | 124 ++++++++++++++++++-
> > src/device.h | 11 +-
> > src/set.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > src/set.h | 14 +++
> > 6 files changed, 482 insertions(+), 11 deletions(-)
> > create mode 100644 src/set.c
> > create mode 100644 src/set.h
> >
> > diff --git a/Makefile.am b/Makefile.am
> > index 0e8cce249c7d..7b010c8159e9 100644
> > --- a/Makefile.am
> > +++ b/Makefile.am
> > @@ -326,7 +326,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
> > src/eir.h src/eir.c \
> > src/adv_monitor.h src/adv_monitor.c \
> > src/battery.h src/battery.c \
> > - src/settings.h src/settings.c
> > + src/settings.h src/settings.c \
> > + src/set.h src/set.c
> > src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
> > gdbus/libgdbus-internal.la \
> > src/libshared-glib.la \
> > diff --git a/src/adapter.c b/src/adapter.c
> > index 538310c1ddc6..ae0eb364bad5 100644
> > --- a/src/adapter.c
> > +++ b/src/adapter.c
> > @@ -4387,8 +4387,8 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys)
> > if (dev) {
> > device_set_paired(dev, info->bdaddr_type);
> > device_set_bonded(dev, info->bdaddr_type);
> > - device_set_ltk_enc_size(dev, info->enc_size);
> > - device_set_ltk_enc_size(dev, info->enc_size);
> > + device_set_ltk(dev, info->val, info->central,
> > + info->enc_size);
> > }
> > }
> >
> > @@ -8657,7 +8657,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
> > device_set_bonded(device, addr->type);
> > }
> >
> > - device_set_ltk_enc_size(device, ev->key.enc_size);
> > + device_set_ltk(device, ev->key.val, ev->key.central, ev->key.enc_size);
> >
> > bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
> > }
> > diff --git a/src/device.c b/src/device.c
> > index cb16d37c1ae1..caee2183b9d8 100644
> > --- a/src/device.c
> > +++ b/src/device.c
> > @@ -64,6 +64,7 @@
> > #include "storage.h"
> > #include "eir.h"
> > #include "settings.h"
> > +#include "set.h"
> >
> > #define DISCONNECT_TIMER 2
> > #define DISCOVERY_TIMER 1
> > @@ -159,11 +160,24 @@ struct bearer_state {
> > time_t last_seen;
> > };
> >
> > +struct ltk_info {
> > + uint8_t key[16];
> > + bool central;
> > + uint8_t enc_size;
> > +};
> > +
> > struct csrk_info {
> > uint8_t key[16];
> > uint32_t counter;
> > };
> >
> > +struct sirk_info {
> > + uint8_t encrypted;
> > + uint8_t key[16];
> > + uint8_t size;
> > + uint8_t rank;
> > +};
> > +
> > enum {
> > WAKE_FLAG_DEFAULT = 0,
> > WAKE_FLAG_ENABLED,
> > @@ -253,7 +267,8 @@ struct btd_device {
> >
> > struct csrk_info *local_csrk;
> > struct csrk_info *remote_csrk;
> > - uint8_t ltk_enc_size;
> > + struct ltk_info *ltk;
> > + struct sirk_info *sirk;
> >
> > sdp_list_t *tmp_records;
> >
> > @@ -386,6 +401,21 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
> > g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
> > }
> >
> > +static void store_sirk(struct sirk_info *sirk, GKeyFile *key_file)
> > +{
> > + const char *group = "SetIdentityResolvingKey";
> > + char key[33];
> > + int i;
> > +
> > + for (i = 0; i < 16; i++)
> > + sprintf(key + (i * 2), "%2.2X", sirk->key[i]);
> > +
> > + g_key_file_set_boolean(key_file, group, "Encrypted", sirk->encrypted);
> > + g_key_file_set_string(key_file, group, "Key", key);
> > + g_key_file_set_integer(key_file, group, "Size", sirk->size);
> > + g_key_file_set_integer(key_file, group, "Size", sirk->rank);
>
> This should be "Rank", not "Size".
>
> > +}
> > +
> > static gboolean store_device_info_cb(gpointer user_data)
> > {
> > struct btd_device *device = user_data;
> > @@ -483,6 +513,9 @@ static gboolean store_device_info_cb(gpointer user_data)
> > if (device->remote_csrk)
> > store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey");
> >
> > + if (device->sirk)
> > + store_sirk(device->sirk, key_file);
> > +
> > str = g_key_file_to_data(key_file, &length, NULL);
> > if (!g_file_set_contents(filename, str, length, &gerr)) {
> > error("Unable set contents for %s: (%s)", filename,
> > @@ -806,6 +839,7 @@ static void device_free(gpointer user_data)
> >
> > g_free(device->local_csrk);
> > g_free(device->remote_csrk);
> > + free(device->ltk);
> > g_free(device->path);
> > g_free(device->alias);
> > free(device->modalias);
> > @@ -1792,10 +1826,43 @@ bool device_is_disconnecting(struct btd_device *device)
> > return device->disconn_timer > 0;
> > }
> >
> > -void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size)
> > +void device_set_ltk(struct btd_device *device, const uint8_t val[16],
> > + bool central, uint8_t enc_size)
> > {
> > - device->ltk_enc_size = enc_size;
> > - bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
> > + if (!device->ltk)
> > + device->ltk = new0(struct ltk_info, 1);
> > +
> > + memcpy(device->ltk->key, val, sizeof(device->ltk->key));
> > + device->ltk->central = central;
> > + device->ltk->enc_size = enc_size;
> > + bt_att_set_enc_key_size(device->att, enc_size);
> > +
> > + if (device->sirk && device->sirk->encrypted)
> > + btd_set_add_device(device, device->ltk->key, device->sirk->key,
> > + device->sirk->size,
> > + device->sirk->rank);
> > +}
> > +
> > +bool btd_device_add_set(struct btd_device *device, bool encrypted,
> > + uint8_t sirk[16], uint8_t size, uint8_t rank)
> > +{
> > + if (encrypted && !device->ltk)
> > + return false;
> > +
> > + if (!device->sirk)
> > + device->sirk = new0(struct sirk_info, 1);
> > +
> > + device->sirk->encrypted = encrypted;
> > + memcpy(device->sirk->key, sirk, sizeof(device->sirk->key));
> > + device->sirk->size = size;
> > + device->sirk->rank = rank;
> > +
> > + btd_set_add_device(device, encrypted ? device->ltk->key : NULL, sirk,
> > + size, rank);
> > +
> > + store_device_info(device);
> > +
> > + return true;
> > }
> >
> > static void device_set_auto_connect(struct btd_device *device, gboolean enable)
> > @@ -3290,6 +3357,39 @@ fail:
> > return NULL;
> > }
> >
> > +static struct sirk_info *load_sirk(GKeyFile *key_file)
> > +{
> > + const char *group = "SetIdentityResolvingKey";
> > + struct sirk_info *sirk;
> > + char *str;
> > + int i;
> > +
> > + str = g_key_file_get_string(key_file, group, "Key", NULL);
> > + if (!str)
> > + return NULL;
> > +
> > + sirk = g_new0(struct sirk_info, 1);
> > +
> > + for (i = 0; i < 16; i++) {
> > + if (sscanf(str + (i * 2), "%2hhx", &sirk->key[i]) != 1)
> > + goto fail;
> > + }
> > +
> > +
> > + sirk->encrypted = g_key_file_get_boolean(key_file, group, "Encrypted",
> > + NULL);
> > + sirk->size = g_key_file_get_integer(key_file, group, "Size", NULL);
> > + sirk->rank = g_key_file_get_integer(key_file, group, "Rank", NULL);
> > + g_free(str);
> > +
> > + return sirk;
> > +
> > +fail:
> > + g_free(str);
> > + g_free(sirk);
> > + return NULL;
> > +}
> > +
> > static void load_services(struct btd_device *device, char **uuids)
> > {
> > char **uuid;
> > @@ -3430,6 +3530,12 @@ static void load_info(struct btd_device *device, const char *local,
> >
> > device->local_csrk = load_csrk(key_file, "LocalSignatureKey");
> > device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey");
> > +
> > + device->sirk = load_sirk(key_file);
> > + if (device->sirk && !device->sirk->encrypted)
> > + btd_set_add_device(device, NULL, device->sirk->key,
> > + device->sirk->size,
> > + device->sirk->rank);
> > }
> >
> > g_strfreev(techno);
> > @@ -5207,7 +5313,9 @@ static void gatt_server_init(struct btd_device *device,
> > return;
> > }
> >
> > - bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
> > + if (device->ltk)
> > + bt_att_set_enc_key_size(device->att, device->ltk->enc_size);
> > +
> > bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
> >
> > btd_gatt_database_server_connected(database, device->server);
> > @@ -6928,3 +7036,9 @@ int8_t btd_device_get_volume(struct btd_device *device)
> > {
> > return device->volume;
> > }
> > +
> > +void btd_device_foreach_ad(struct btd_device *dev, bt_ad_func_t func,
> > + void *data)
> > +{
> > + bt_ad_foreach_data(dev->ad, func, data);
> > +}
> > diff --git a/src/device.h b/src/device.h
> > index 9e81fda9e948..96347ff229cc 100644
> > --- a/src/device.h
> > +++ b/src/device.h
> > @@ -128,8 +128,10 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type,
> > bool *remove);
> > void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
> > bool device_is_disconnecting(struct btd_device *device);
> > -void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size);
> > -
> > +void device_set_ltk(struct btd_device *device, const uint8_t val[16],
> > + bool central, uint8_t enc_size);
> > +bool btd_device_add_set(struct btd_device *device, bool encrypted,
> > + uint8_t sirk[16], uint8_t size, uint8_t rank);
> > void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type,
> > uint16_t value);
> > void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le,
> > @@ -188,3 +190,8 @@ void btd_device_cleanup(void);
> >
> > void btd_device_set_volume(struct btd_device *dev, int8_t volume);
> > int8_t btd_device_get_volume(struct btd_device *dev);
> > +
> > +typedef void (*bt_device_ad_func_t)(void *data, void *user_data);
> > +
> > +void btd_device_foreach_ad(struct btd_device *dev, bt_device_ad_func_t func,
> > + void *data);
> > diff --git a/src/set.c b/src/set.c
> > new file mode 100644
> > index 000000000000..6b3cd82651e6
> > --- /dev/null
> > +++ b/src/set.c
> > @@ -0,0 +1,335 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + *
> > + * BlueZ - Bluetooth protocol stack for Linux
> > + *
> > + * Copyright (C) 2023 Intel Corporation
> > + *
> > + *
> > + */
> > +
> > +#ifdef HAVE_CONFIG_H
> > +#include <config.h>
> > +#endif
> > +
> > +#define _GNU_SOURCE
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <stdbool.h>
> > +#include <errno.h>
> > +
> > +#include <glib.h>
> > +#include <dbus/dbus.h>
> > +
> > +#include "gdbus/gdbus.h"
> > +#include "src/shared/util.h"
> > +#include "src/shared/queue.h"
> > +#include "src/shared/ad.h"
> > +#include "src/shared/crypto.h"
> > +
> > +#include "log.h"
> > +#include "error.h"
> > +#include "adapter.h"
> > +#include "device.h"
> > +#include "dbus-common.h"
> > +#include "set.h"
> > +
> > +static struct queue *set_list;
> > +
> > +struct btd_device_set {
> > + struct btd_adapter *adapter;
> > + char *path;
> > + uint8_t sirk[16];
> > + uint8_t size;
> > + uint8_t rank;
> > + bool auto_connect;
> > + struct queue *devices;
> > + struct btd_device *device;
> > +};
> > +
> > +static DBusMessage *set_disconnect(DBusConnection *conn, DBusMessage *msg,
> > + void *user_data)
> > +{
> > + /* TODO */
> > + return NULL;
> > +}
> > +
> > +static DBusMessage *set_connect(DBusConnection *conn, DBusMessage *msg,
> > + void *user_data)
> > +{
> > + /* TODO */
> > + return NULL;
> > +}
> > +
> > +static const GDBusMethodTable set_methods[] = {
> > + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Disconnect", NULL, NULL,
> > + set_disconnect) },
> > + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("Connect", NULL, NULL,
> > + set_connect) },
> > + {}
> > +};
> > +
> > +static gboolean get_auto_connect(const GDBusPropertyTable *property,
> > + DBusMessageIter *iter, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > +
> > + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
> > + &set->auto_connect);
> > +
> > + return TRUE;
> > +}
> > +
> > +static void set_auto_connect(const GDBusPropertyTable *property,
> > + DBusMessageIter *iter,
> > + GDBusPendingPropertySet id, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > + dbus_bool_t b;
> > +
> > + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
> > + g_dbus_pending_property_error(id,
> > + ERROR_INTERFACE ".InvalidArguments",
> > + "Invalid arguments in method call");
> > + return;
> > + }
> > +
> > + dbus_message_iter_get_basic(iter, &b);
> > +
> > + set->auto_connect = b ? true : false;
> > +
> > + g_dbus_pending_property_success(id);
> > +}
> > +
> > +static void append_device(void *data, void *user_data)
> > +{
> > + struct btd_device *device = data;
> > + const char *path = device_get_path(device);
> > + DBusMessageIter *entry = user_data;
> > +
> > + dbus_message_iter_append_basic(entry, DBUS_TYPE_OBJECT_PATH, &path);
> > +}
> > +
> > +static gboolean get_devices(const GDBusPropertyTable *property,
> > + DBusMessageIter *iter, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > + DBusMessageIter entry;
> > +
> > + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
> > + DBUS_TYPE_OBJECT_PATH_AS_STRING,
> > + &entry);
> > +
> > + queue_foreach(set->devices, append_device, &entry);
> > +
> > + dbus_message_iter_close_container(iter, &entry);
> > +
> > + return TRUE;
> > +}
> > +
> > +static gboolean get_size(const GDBusPropertyTable *property,
> > + DBusMessageIter *iter, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > +
> > + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->size);
> > +
> > + return TRUE;
> > +}
> > +
> > +static gboolean get_rank(const GDBusPropertyTable *property,
> > + DBusMessageIter *iter, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > +
> > + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &set->rank);
> > +
> > + return TRUE;
> > +}
> > +
> > +static const GDBusPropertyTable set_properties[] = {
> > + { "AutoConnect", "b", get_auto_connect, set_auto_connect, NULL,
> > + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> > + { "Devices", "ao", get_devices, NULL, NULL,
> > + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> > + { "Size", "y", get_size, NULL, NULL,
> > + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> > + { "Rank", "y", get_rank, NULL, NULL,
> > + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
> > + {}
> > +};
> > +
> > +static void set_free(void *data)
> > +{
> > + struct btd_device_set *set = data;
> > +
> > + queue_destroy(set->devices, NULL);
> > + free(set);
> > +}
> > +
> > +static struct btd_device_set *set_new(struct btd_device *device,
> > + uint8_t sirk[16], uint8_t size,
> > + uint8_t rank)
> > +{
> > + struct btd_device_set *set;
> > +
> > + set = new0(struct btd_device_set, 1);
> > + set->adapter = device_get_adapter(device);
> > + memcpy(set->sirk, sirk, sizeof(set->sirk));
> > + set->size = size;
> > + set->rank = rank;
> > + set->auto_connect = true;
> > + set->devices = queue_new();
> > + queue_push_tail(set->devices, device);
> > + set->path = g_strdup_printf("%s/set_%02x%02x%02x%02x%02x%02x%02x%02x"
> > + "%02x%02x%02x%02x%02x%02x%02x%02x",
> > + adapter_get_path(set->adapter),
> > + sirk[15], sirk[14], sirk[13], sirk[12],
> > + sirk[11], sirk[10], sirk[9], sirk[8],
> > + sirk[7], sirk[6], sirk[5], sirk[4],
> > + sirk[3], sirk[2], sirk[1], sirk[0]);
> > +
> > + DBG("Creating set %s", set->path);
> > +
> > + if (g_dbus_register_interface(btd_get_dbus_connection(),
> > + set->path, BTD_DEVICE_SET_INTERFACE,
> > + set_methods, NULL,
> > + set_properties, set,
> > + set_free) == FALSE) {
> > + error("Unable to register set interface");
> > + set_free(set);
> > + return NULL;
> > + }
> > +
> > + return set;
> > +}
> > +
> > +static struct btd_device_set *set_find(struct btd_device *device,
> > + uint8_t sirk[16])
> > +{
> > + struct btd_adapter *adapter = device_get_adapter(device);
> > + const struct queue_entry *entry;
> > +
> > + for (entry = queue_get_entries(set_list); entry; entry = entry->next) {
> > + struct btd_device_set *set = entry->data;
> > +
> > + if (set->adapter != adapter)
> > + continue;
> > +
> > + if (!memcmp(set->sirk, sirk, sizeof(set->sirk)))
> > + return set;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +static void set_connect_next(struct btd_device_set *set)
> > +{
> > + const struct queue_entry *entry;
> > +
> > + for (entry = queue_get_entries(set->devices); entry;
> > + entry = entry->next) {
> > + struct btd_device *device = entry->data;
> > +
> > + /* Only connect one at time(?) */
> > + if (!device_connect_le(device))
> > + return;
> > + }
> > +}
> > +
> > +static void set_add(struct btd_device_set *set, struct btd_device *device)
> > +{
> > + /* Check if device is already part of the set then skip to connect */
> > + if (queue_find(set->devices, NULL, device))
> > + goto done;
> > +
> > + DBG("set %s device %s", set->path, device_get_path(device));
> > +
> > + queue_push_tail(set->devices, device);
> > + g_dbus_emit_property_changed(btd_get_dbus_connection(), set->path,
> > + BTD_DEVICE_SET_INTERFACE, "Devices");
> > +
> > +done:
> > + /* Check if set is marked to auto-connect */
> > + if (btd_device_is_connected(device) && set->auto_connect)
> > + set_connect_next(set);
> > +}
> > +
> > +static void foreach_rsi(void *data, void *user_data)
> > +{
> > + struct bt_ad_data *ad = data;
> > + struct btd_device_set *set = user_data;
> > + struct bt_crypto *crypto;
> > + uint8_t res[3];
> > +
> > + if (ad->type != BT_AD_CSIP_RSI || ad->len < 6)
> > + return;
> > +
> > + crypto = bt_crypto_new();
> > + if (!crypto)
> > + return;
> > +
> > + if (!bt_crypto_sih(crypto, set->sirk, ad->data + 3, res)) {
> > + bt_crypto_unref(crypto);
> > + return;
> > + }
> > +
> > + bt_crypto_unref(crypto);
> > +
> > + if (!memcmp(ad->data, res, sizeof(res)))
> > + device_connect_le(set->device);
> > +}
> > +
> > +static void foreach_device(struct btd_device *device, void *data)
> > +{
> > + struct btd_device_set *set = data;
> > +
> > + /* Check if device is already part of the set then skip */
> > + if (queue_find(set->devices, NULL, device))
> > + return;
> > +
> > + set->device = device;
> > +
> > + btd_device_foreach_ad(device, foreach_rsi, set);
> > +}
> > +
> > +void btd_set_add_device(struct btd_device *device, uint8_t *key,
> > + uint8_t sirk[16], uint8_t size, uint8_t rank)
> > +{
> > + struct btd_device_set *set;
> > +
> > + /* In case key has been set it means SIRK is encrypted */
> > + if (key) {
> > + struct bt_crypto *crypto = bt_crypto_new();
> > +
> > + if (!crypto)
> > + return;
> > +
> > + /* sef and sdf are symmetric */
> > + bt_crypto_sef(crypto, key, sirk, sirk);
> > +
> > + bt_crypto_unref(crypto);
> > + }
> > +
> > + /* Check if DeviceSet already exists */
> > + set = set_find(device, sirk);
> > + if (set) {
> > + set_add(set, device);
> > + return;
> > + }
> > +
> > + set = set_new(device, sirk, size, rank);
> > + if (!set)
> > + return;
> > +
> > + if (!set_list)
> > + set_list = queue_new();
> > +
> > + queue_push_tail(set_list, set);
> > +
> > + /* Attempt to add devices which have matching RSI */
> > + btd_adapter_for_each_device(device_get_adapter(device), foreach_device,
> > + set);
> > +}
> > diff --git a/src/set.h b/src/set.h
> > new file mode 100644
> > index 000000000000..c3831ef9ca66
> > --- /dev/null
> > +++ b/src/set.h
> > @@ -0,0 +1,14 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + *
> > + * BlueZ - Bluetooth protocol stack for Linux
> > + *
> > + * Copyright (C) 2023 Intel Corporation
> > + *
> > + *
> > + */
> > +
> > +#define BTD_DEVICE_SET_INTERFACE "org.bluez.DeviceSet1"
> > +
> > +void btd_set_add_device(struct btd_device *device, uint8_t *ltk,
> > + uint8_t sirk[16], uint8_t size, uint8_t rank);
>
> --
> Pauli Virtanen



--
Luiz Augusto von Dentz