diff mbox series

[BlueZ,v1,2/3] main.conf: Add GATT.ExportClaimedServices

Message ID 20241209205843.1394081-2-luiz.dentz@gmail.com
State New
Headers show
Series [BlueZ,v1,1/3] shared/gatt-db: Fix possible crash on gatt_db_clone | expand

Commit Message

Luiz Augusto von Dentz Dec. 9, 2024, 8:58 p.m. UTC
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds GATT.ExportClaimedService which can be used to allow access to
service under control of bluetoothd(1) since often times it should be
safe to allow read-only access as it shouldn't interfere with each
other, but this one step further and allow the systems to be configured
with read-write as well just in case some sort of workaround is needed,
or for debugging purposes.

Since this is now a proper system policy this removes the expections from
battery and deviceinfo introduced by 713f6f09f017
("profile: Add exception to battery profile for external access") and
0cef5a7996db ("deviceinfo: Enable external flag").
---
 profiles/battery/battery.c       |  1 -
 profiles/deviceinfo/deviceinfo.c |  1 -
 src/btd.h                        |  7 ++++++
 src/gatt-client.c                | 37 ++++++++++++++++++++++++++++++--
 src/main.c                       | 30 ++++++++++++++++++++++++++
 src/main.conf                    |  5 +++++
 6 files changed, 77 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 02d024d927ec..5318d40d12b4 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -327,7 +327,6 @@  static struct btd_profile batt_profile = {
 	.device_remove	= batt_remove,
 	.accept		= batt_accept,
 	.disconnect	= batt_disconnect,
-	.external	= true,
 };
 
 static int batt_init(void)
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index b6dc0ab2e207..0cdf2947e0a3 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -138,7 +138,6 @@  static int deviceinfo_disconnect(struct btd_service *service)
 static struct btd_profile deviceinfo_profile = {
 	.name		= "deviceinfo",
 	.remote_uuid	= DEVICE_INFORMATION_UUID,
-	.external	= true,
 	.device_probe	= deviceinfo_probe,
 	.device_remove	= deviceinfo_remove,
 	.accept		= deviceinfo_accept,
diff --git a/src/btd.h b/src/btd.h
index 07205aa69486..a443f66f6cd8 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -42,6 +42,12 @@  enum sc_mode_t {
 	SC_ONLY,
 };
 
+enum bt_gatt_export_t {
+	BT_GATT_EXPORT_OFF,
+	BT_GATT_EXPORT_READ_ONLY,
+	BT_GATT_EXPORT_READ_WRITE,
+};
+
 struct btd_br_defaults {
 	uint16_t	page_scan_type;
 	uint16_t	page_scan_interval;
@@ -147,6 +153,7 @@  struct btd_opts {
 	uint16_t	gatt_mtu;
 	uint8_t		gatt_channels;
 	bool		gatt_client;
+	enum bt_gatt_export_t gatt_export;
 	enum mps_mode_t	mps;
 
 	struct btd_avdtp_opts avdtp;
diff --git a/src/gatt-client.c b/src/gatt-client.c
index a67e04eee81e..6f22bbb490a7 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -66,6 +66,7 @@  struct btd_gatt_client {
 struct service {
 	struct btd_gatt_client *client;
 	bool primary;
+	bool claimed;
 	uint16_t start_handle;
 	uint16_t end_handle;
 	bt_uuid_t uuid;
@@ -616,6 +617,14 @@  static DBusMessage *descriptor_write_value(DBusConnection *conn,
 	if (parse_options(&iter, &offset, NULL))
 		return btd_error_invalid_args(msg);
 
+	/*
+	 * Check if service was previously claimed by a plugin and if we shall
+	 * consider it read-only, in that case return not authorized.
+	 */
+	if (desc->chrc->service->claimed &&
+			btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY)
+		return btd_error_not_authorized(msg);
+
 	/*
 	 * Don't allow writing to Client Characteristic Configuration
 	 * descriptors. We achieve this through the StartNotify and StopNotify
@@ -1046,6 +1055,14 @@  static DBusMessage *characteristic_write_value(DBusConnection *conn,
 	if (parse_options(&iter, &offset, &type))
 		return btd_error_invalid_args(msg);
 
+	/*
+	 * Check if service was previously claimed by a plugin and if we shall
+	 * consider it read-only, in that case return not authorized.
+	 */
+	if (chrc->service->claimed &&
+			btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY)
+		return btd_error_not_authorized(msg);
+
 	/*
 	 * Decide which write to use based on characteristic properties. For now
 	 * we don't perform signed writes since gatt-client doesn't support them
@@ -1295,6 +1312,14 @@  static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
 	if (!gatt)
 		return btd_error_failed(msg, "Not connected");
 
+	/*
+	 * Check if service was previously claimed by a plugin and if we shall
+	 * consider it read-only, in that case return not authorized.
+	 */
+	if (chrc->service->claimed &&
+			btd_opts.gatt_export == BT_GATT_EXPORT_READ_ONLY)
+		return btd_error_not_authorized(msg);
+
 	if (chrc->write_io)
 		return btd_error_not_permitted(msg, "Write acquired");
 
@@ -1954,6 +1979,7 @@  static struct service *service_create(struct gatt_db_attribute *attr,
 	service->chrcs = queue_new();
 	service->incl_services = queue_new();
 	service->client = client;
+	service->claimed = gatt_db_service_get_claimed(attr);
 
 	gatt_db_attribute_get_service_data(attr, &service->start_handle,
 							&service->end_handle,
@@ -2097,8 +2123,15 @@  static void export_service(struct gatt_db_attribute *attr, void *user_data)
 	struct btd_gatt_client *client = user_data;
 	struct service *service;
 
-	if (gatt_db_service_get_claimed(attr))
-		return;
+	if (gatt_db_service_get_claimed(attr)) {
+		switch (btd_opts.gatt_export) {
+		case BT_GATT_EXPORT_OFF:
+			return;
+		case BT_GATT_EXPORT_READ_ONLY:
+		case BT_GATT_EXPORT_READ_WRITE:
+			break;
+		}
+	}
 
 	service = service_create(attr, client);
 	if (!service)
diff --git a/src/main.c b/src/main.c
index 41c3271a7457..b92b22e41d4c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -148,6 +148,7 @@  static const char *gatt_options[] = {
 	"ExchangeMTU",
 	"Channels",
 	"Client",
+	"ExportClaimedServices",
 	NULL
 };
 
@@ -1066,6 +1067,33 @@  static void parse_gatt_cache(GKeyFile *config)
 	g_free(str);
 }
 
+static enum bt_gatt_export_t parse_gatt_export_str(const char *str)
+{
+	if (!strcmp(str, "no") || !strcmp(str, "false") ||
+				!strcmp(str, "off")) {
+		return BT_GATT_EXPORT_OFF;
+	} else if (!strcmp(str, "read-only")) {
+		return BT_GATT_EXPORT_READ_ONLY;
+	} else if (!strcmp(str, "read-write")) {
+		return BT_GATT_EXPORT_READ_WRITE;
+	}
+
+	DBG("Invalid value for ExportClaimedServices=%s", str);
+	return BT_GATT_EXPORT_READ_ONLY;
+}
+
+static void parse_gatt_export(GKeyFile *config)
+{
+	char *str = NULL;
+
+	parse_config_string(config, "GATT", "ExportClaimedServices", &str);
+	if (!str)
+		return;
+
+	btd_opts.gatt_export = parse_gatt_export_str(str);
+	g_free(str);
+}
+
 static void parse_gatt(GKeyFile *config)
 {
 	parse_gatt_cache(config);
@@ -1075,6 +1103,7 @@  static void parse_gatt(GKeyFile *config)
 	parse_config_u8(config, "GATT", "Channels", &btd_opts.gatt_channels,
 				1, 5);
 	parse_config_bool(config, "GATT", "Client", &btd_opts.gatt_client);
+	parse_gatt_export(config);
 }
 
 static void parse_csis_sirk(GKeyFile *config)
@@ -1219,6 +1248,7 @@  static void init_defaults(void)
 	btd_opts.gatt_mtu = BT_ATT_MAX_LE_MTU;
 	btd_opts.gatt_channels = 1;
 	btd_opts.gatt_client = true;
+	btd_opts.gatt_export = BT_GATT_EXPORT_READ_ONLY;
 
 	btd_opts.avdtp.session_mode = BT_IO_MODE_BASIC;
 	btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;
diff --git a/src/main.conf b/src/main.conf
index fff13ed2ff31..80e72e18f749 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -262,6 +262,11 @@ 
 # Default to 1
 #Channels = 1
 
+# Export claimed services by plugins
+# Possible values: no, read-only, read-write
+# Default: read-only
+#ExportClaimedServices = read-only
+
 [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