[PATCH BlueZ v3 6/7] device: unify admin allowlist checks for device services

Frédéric Danis <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth
Message-ID <[email protected]>
Consolidate device-side admin policy handling into one coherent model so
incoming and outgoing A2DP behavior follows the configured allowlist
consistently.

Why:
- The initial profile-aware adapter mapping fixed role-inverted A2DP cases
  for some paths, but device-side service gating still used mixed criteria.
- Host-initiated Connect() could evaluate A2DP services with the wrong UUID
  perspective, leading to valid flows being blocked (or blocked flows being
  considered valid) under partial allowlists.

What changed:
- Use direct UUID allowlist checks for device service eligibility updates.
- Add A2DP role-aware mapping for device policy UUID selection:
  - a2dp-sink -> A2DP Source UUID (110a)
  - a2dp-source -> A2DP Sink UUID (110b)
- Apply the same policy UUID resolution in:
  - btd_device_all_services_allowed()
  - btd_device_update_allowed_services()

Result:
- Device-service policy decisions now align with intended local A2DP role
  semantics for both remote-initiated and host-initiated connection paths.
- Admin allowlist enforcement remains strict while eliminating the observed
  false block during host->remote A2DP connect attempts.

Assisted-by: GPT:GPT-5.3-Codex
---
 src/adapter.c |  6 +++---
 src/adapter.h |  3 +++
 src/device.c  | 35 +++++++++++++++++++++++++++++++----
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 0cc87f649..138c57800 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -5403,7 +5403,7 @@ static const char *profile_allowlist_uuid(const struct btd_profile *profile)
 	return NULL;
 }
 
-static bool adapter_profile_is_allowed(struct btd_adapter *adapter,
+bool btd_adapter_is_profile_allowed(struct btd_adapter *adapter,
 					const struct btd_profile *profile)
 {
 	const char *uuid = profile_allowlist_uuid(profile);
@@ -5422,7 +5422,7 @@ static void probe_profile(struct btd_profile *profile, void *data)
 	if (profile->adapter_probe == NULL)
 		return;
 
-	if (!adapter_profile_is_allowed(adapter, profile)) {
+	if (!btd_adapter_is_profile_allowed(adapter, profile)) {
 		DBG("%s blocked by admin allowlist", profile->name);
 		return;
 	}
@@ -5447,7 +5447,7 @@ static void reapply_profile(struct btd_profile *profile, void *data)
 
 	active = g_slist_find(adapter->profiles, profile) != NULL;
 
-	if (adapter_profile_is_allowed(adapter, profile)) {
+	if (btd_adapter_is_profile_allowed(adapter, profile)) {
 		if (!active)
 			probe_profile(profile, adapter);
 		return;
diff --git a/src/adapter.h b/src/adapter.h
index a1c887c45..583168f4b 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -27,6 +27,7 @@
 
 struct btd_adapter;
 struct btd_device;
+struct btd_profile;
 struct queue;
 
 struct btd_adapter *btd_adapter_get_default(void);
@@ -299,6 +300,8 @@ bool btd_adapter_set_allowed_uuids(struct btd_adapter *adapter,
 							struct queue *uuids);
 bool btd_adapter_is_uuid_allowed(struct btd_adapter *adapter,
 							const char *uuid_str);
+bool btd_adapter_is_profile_allowed(struct btd_adapter *adapter,
+					const struct btd_profile *profile);
 void btd_adapter_reapply_allowed_uuids(struct btd_adapter *adapter);
 
 void btd_adapter_load_conn_param(struct btd_adapter *adapter,
diff --git a/src/device.c b/src/device.c
index 65d84be56..c396372be 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2547,21 +2547,44 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
 	return NULL;
 }
 
+static const char *service_policy_uuid(const struct btd_profile *profile)
+{
+	if (!profile)
+		return NULL;
+
+	/*
+	 * For A2DP device services, apply admin policy by local role UUID:
+	 * - a2dp-sink profile is local source role  (110a)
+	 * - a2dp-source profile is local sink role  (110b)
+	 */
+	if (profile->name) {
+		if (!strcmp(profile->name, "a2dp-sink"))
+			return A2DP_SOURCE_UUID;
+
+		if (!strcmp(profile->name, "a2dp-source"))
+			return A2DP_SINK_UUID;
+	}
+
+	return profile->remote_uuid;
+}
+
 bool btd_device_all_services_allowed(struct btd_device *dev)
 {
 	GSList *l;
 	struct btd_adapter *adapter = dev->adapter;
 	struct btd_service *service;
 	struct btd_profile *profile;
+	const char *uuid;
 
 	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
 		service = l->data;
 		profile = btd_service_get_profile(service);
+		uuid = service_policy_uuid(profile);
 
-		if (!profile || !profile->auto_connect)
+		if (!profile || !profile->auto_connect || !uuid)
 			continue;
 
-		if (!btd_adapter_is_uuid_allowed(adapter, profile->remote_uuid))
+		if (!btd_adapter_is_uuid_allowed(adapter, uuid))
 			return false;
 	}
 
@@ -2575,6 +2598,7 @@ void btd_device_update_allowed_services(struct btd_device *dev)
 	struct btd_profile *profile;
 	GSList *l;
 	bool is_allowed;
+	const char *uuid;
 	char addr[18];
 
 	/* If service discovery is ongoing, let the service discovery complete
@@ -2590,9 +2614,12 @@ void btd_device_update_allowed_services(struct btd_device *dev)
 	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
 		service = l->data;
 		profile = btd_service_get_profile(service);
+		uuid = service_policy_uuid(profile);
+
+		if (!profile || !uuid)
+			continue;
 
-		is_allowed = btd_adapter_is_uuid_allowed(adapter,
-							profile->remote_uuid);
+		is_allowed = btd_adapter_is_uuid_allowed(adapter, uuid);
 		btd_service_set_allowed(service, is_allowed);
 	}
 }
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.