All of lore.kernel.org
 help / color / mirror / Atom feed
* [Bluez PATCH 0/8] Hi Linux-bluetooth,
@ 2021-04-01 10:24 Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 1/8] doc: add description of SetServiceAllowList Howard Chung
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Yun-Hao Chung

From: Yun-Hao Chung <howardchung@chromium.org>


This series is to add a new method |SetServiceAllowList| in D-Bus API to
control what services can be connected by specifying UUID allow list.
Users can query a device property |IsBlockedByPolicy| to tell if some
of the services are currently restricted. Since we plan to add more
methods to limit different bluetooth functionalities, we also introduce
a new interface for this purpose. This series has been tested via sending
commands with dbus-send manually on chromebook octopus and eve.

Thanks,
Howard


Howard Chung (7):
  doc: add description of SetServiceAllowList
  lib: add hash functions for bt_uuid_t
  core: add AdminPolicy Interface
  input/hog: block connection by policy
  audio: Remove Media1 interface when a2dp source disallowed
  core: add properties IsBlockedByPolicy and ServiceAllowList
  core: store ServiceAllowList into settings

Sonny Sasaka (1):
  core: Initialize uuid_str_arr to NULL

 Makefile.am              |   3 +-
 doc/admin_policy-api.txt |  34 +++++
 doc/device-api.txt       |   7 +
 lib/uuid.c               |  21 +++
 lib/uuid.h               |   3 +
 profiles/audio/a2dp.c    |   2 +
 profiles/audio/avrcp.c   |   3 +
 profiles/input/hog.c     |  22 +++
 src/adapter.c            | 148 +++++++++++++++++++-
 src/adapter.h            |   5 +
 src/admin_policy.c       | 292 +++++++++++++++++++++++++++++++++++++++
 src/admin_policy.h       |  23 +++
 src/device.c             |  79 ++++++++++-
 src/device.h             |   2 +
 src/profile.c            |  39 ++++++
 src/profile.h            |   5 +
 src/service.c            |  21 +++
 src/service.h            |   1 +
 18 files changed, 705 insertions(+), 5 deletions(-)
 create mode 100644 doc/admin_policy-api.txt
 create mode 100644 src/admin_policy.c
 create mode 100644 src/admin_policy.h

-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Bluez PATCH 1/8] doc: add description of SetServiceAllowList
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 2/8] lib: add hash functions for bt_uuid_t Howard Chung
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This adds description of SetServiceAllowList.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 doc/admin_policy-api.txt | 34 ++++++++++++++++++++++++++++++++++
 doc/device-api.txt       |  7 +++++++
 2 files changed, 41 insertions(+)
 create mode 100644 doc/admin_policy-api.txt

diff --git a/doc/admin_policy-api.txt b/doc/admin_policy-api.txt
new file mode 100644
index 000000000000..dd957351506a
--- /dev/null
+++ b/doc/admin_policy-api.txt
@@ -0,0 +1,34 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+
+Adapter hierarchy
+=================
+
+Service		org.bluez
+Interface	org.bluez.AdminPolicy1
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void SetServiceAllowList(array{string} UUIDs) [experimental]
+
+			This method sets the service allowlist by specifying
+			service UUIDs.
+
+			When SetServiceAllowList is called, bluez will first
+			disconnect all the pending/existing connections and
+			block future incoming and outgoing connections to
+			the service in UUIDs for all of the clients once it
+			finished.
+
+			Any subsequent calls to this method will supersede any
+			previously set allowlist values.  Calling this method
+			with an empty array will allow any service UUIDs to be
+			used.
+
+			Possible errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+
+Properties	array{string} ServiceAllowList [readonly, experimental]
+
+			Current value of service allow list.
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 4e824d2dec17..774ee046cc92 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -272,3 +272,10 @@ Properties	string Address [readonly]
 			Example:
 				<Transport Discovery> <Organization Flags...>
 				0x26                   0x01         0x01...
+
+		bool IsBlockedByPolicy [readonly, experimental]
+
+			Indicate whether or not this device is blocked by admin
+			policy. This would be true if any of its auto-connect
+			service does not exist in the ServiceAllowList under
+			org.bluez.AdminPolicy1.
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 2/8] lib: add hash functions for bt_uuid_t
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 1/8] doc: add description of SetServiceAllowList Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 3/8] core: add AdminPolicy Interface Howard Chung
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This adds function GHashFunc and GEqualFunc for bt_uuid_t.
With these functions, we can add uuids into a GHashTable with bt_uuid_t
format.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 lib/uuid.c | 21 +++++++++++++++++++++
 lib/uuid.h |  3 +++
 2 files changed, 24 insertions(+)

diff --git a/lib/uuid.c b/lib/uuid.c
index a09321dc6ed1..0b0ddb3fc9d2 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <glib.h>
 
 #include "lib/bluetooth.h"
 #include "uuid.h"
@@ -120,6 +121,26 @@ int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2)
 	return bt_uuid128_cmp(&u1, &u2);
 }
 
+guint bt_uuid_hash(gconstpointer key)
+{
+	const bt_uuid_t *uuid = key;
+	bt_uuid_t uuid_128;
+	uint64_t *val;
+
+	bt_uuid_to_uuid128(uuid, &uuid_128);
+	val = (uint64_t *)&uuid_128.value.u128;
+
+	return g_int64_hash(val) ^ g_int64_hash(val+1);
+}
+
+gboolean bt_uuid_equal(gconstpointer v1, gconstpointer v2)
+{
+	const bt_uuid_t *uuid1 = v1;
+	const bt_uuid_t *uuid2 = v2;
+
+	return bt_uuid_cmp(uuid1, uuid2) == 0;
+}
+
 /*
  * convert the UUID to string, copying a maximum of n characters.
  */
diff --git a/lib/uuid.h b/lib/uuid.h
index 1a4029b68730..e47ccccb9fd2 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -17,6 +17,7 @@ extern "C" {
 #endif
 
 #include <stdint.h>
+#include <glib.h>
 
 #define GENERIC_AUDIO_UUID	"00001203-0000-1000-8000-00805f9b34fb"
 
@@ -167,6 +168,8 @@ int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value);
 
 int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2);
 void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
+guint bt_uuid_hash(gconstpointer key);
+gboolean bt_uuid_equal(gconstpointer v1, gconstpointer v2);
 
 #define MAX_LEN_UUID_STR 37
 
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 3/8] core: add AdminPolicy Interface
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 1/8] doc: add description of SetServiceAllowList Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 2/8] lib: add hash functions for bt_uuid_t Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 4/8] input/hog: block connection by policy Howard Chung
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This patch adds a new interface org.bluez.AdminPolicy for Administrator
to have better control on Bluetooth behaviors.

This patch also adds a method SetServiceAllowlist under AdminPolicy
to only allow incoming and outgoing connections to the service in the
allowlist for all of the clients.

For existing connections, bluez will send disconnect request to all of
them in case some of them are not allowed by the policy.

For outgoing connections, services not in the allowlist would be removed
from its pending list when trying to connect.

For incoming connections, profiles not in the allowlist would be
unregistered when policy is set and would be re-registered when policy
is relaxed.

For GATT profiles, if it is a system defined profile, e.g. HoG, we
disallow the connection if any of its mandatory service is not in the
allowlist. Since each profile defines different mandatory
services, we'll handle it profile by profile in the future patches.
If this is not a system defined profile, we only accept its service
connection if it is in allowlist.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 Makefile.am        |   3 +-
 src/adapter.c      |  45 ++++++++-
 src/adapter.h      |   4 +
 src/admin_policy.c | 240 +++++++++++++++++++++++++++++++++++++++++++++
 src/admin_policy.h |  23 +++++
 src/device.c       |  51 +++++++++-
 src/device.h       |   1 +
 src/profile.c      |  39 ++++++++
 src/profile.h      |   5 +
 src/service.c      |  21 ++++
 src/service.h      |   1 +
 11 files changed, 428 insertions(+), 5 deletions(-)
 create mode 100644 src/admin_policy.c
 create mode 100644 src/admin_policy.h

diff --git a/Makefile.am b/Makefile.am
index 7fce2e7c04ad..5a2b65783591 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -297,7 +297,8 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
 			src/dbus-common.c src/dbus-common.h \
 			src/eir.h src/eir.c \
 			src/adv_monitor.h src/adv_monitor.c \
-			src/battery.h src/battery.c
+			src/battery.h src/battery.c \
+			src/admin_policy.h src/admin_policy.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 2fa06b73c240..5531ed0e2246 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -66,6 +66,7 @@
 #include "gatt-database.h"
 #include "advertising.h"
 #include "adv_monitor.h"
+#include "admin_policy.h"
 #include "eir.h"
 #include "battery.h"
 
@@ -260,6 +261,8 @@ struct btd_adapter {
 
 	struct btd_battery_provider_manager *battery_provider_manager;
 
+	struct btd_admin_policy *admin_policy;
+
 	gboolean initialized;
 
 	GSList *pin_callbacks;
@@ -3480,6 +3483,32 @@ static DBusMessage *connect_device(DBusConnection *conn,
 	return NULL;
 }
 
+static void disconnect_device(gpointer data, gpointer user_data)
+{
+	struct btd_device *device = data;
+
+	if (btd_device_is_connected(device))
+		device_request_disconnect(device, NULL);
+}
+
+void btd_adapter_disconnect_all_devices(struct btd_adapter *adapter)
+{
+	g_slist_foreach(adapter->connections, disconnect_device, NULL);
+}
+
+static void update_device_is_blocked_by_policy(void *data, void *user_data)
+{
+	struct btd_device *device = data;
+
+	btd_device_update_is_blocked_by_policy(device);
+}
+
+void btd_adapter_refresh_is_blocked_by_policy(struct btd_adapter *adapter)
+{
+	g_slist_foreach(adapter->devices, update_device_is_blocked_by_policy,
+									NULL);
+}
+
 static const GDBusMethodTable adapter_methods[] = {
 	{ GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
 	{ GDBUS_METHOD("SetDiscoveryFilter",
@@ -4688,7 +4717,7 @@ static void probe_profile(struct btd_profile *profile, void *data)
 	struct btd_adapter *adapter = data;
 	int err;
 
-	if (profile->adapter_probe == NULL)
+	if (profile->adapter_probe == NULL || profile->is_blocked_by_policy)
 		return;
 
 	err = profile->adapter_probe(profile, adapter);
@@ -6372,6 +6401,9 @@ static void adapter_remove(struct btd_adapter *adapter)
 	btd_battery_provider_manager_destroy(adapter->battery_provider_manager);
 	adapter->battery_provider_manager = NULL;
 
+	btd_admin_policy_destroy(adapter->admin_policy);
+	adapter->admin_policy = NULL;
+
 	g_slist_free(adapter->pin_callbacks);
 	adapter->pin_callbacks = NULL;
 
@@ -6873,6 +6905,14 @@ struct agent *adapter_get_agent(struct btd_adapter *adapter)
 	return agent_get(NULL);
 }
 
+bool btd_adapter_uuid_is_allowed(struct btd_adapter *adapter, const char *uuid)
+{
+	if (!adapter || !adapter->admin_policy)
+		return true;
+
+	return btd_admin_policy_uuid_is_allowed(adapter->admin_policy, uuid);
+}
+
 static void adapter_remove_connection(struct btd_adapter *adapter,
 						struct btd_device *device,
 						uint8_t bdaddr_type)
@@ -8660,6 +8700,9 @@ static int adapter_register(struct btd_adapter *adapter)
 		agent_unref(agent);
 	}
 
+	if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)
+		adapter->admin_policy = btd_admin_policy_create(adapter);
+
 	/* Don't start GATT database and advertising managers on
 	 * non-LE controllers.
 	 */
diff --git a/src/adapter.h b/src/adapter.h
index 60b5e3bcca34..8e8c61b7bdf1 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -97,6 +97,8 @@ void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle);
 
 struct agent *adapter_get_agent(struct btd_adapter *adapter);
 
+bool btd_adapter_uuid_is_allowed(struct btd_adapter *adapter, const char *uuid);
+
 struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
 void btd_adapter_unref(struct btd_adapter *adapter);
 
@@ -240,3 +242,5 @@ enum kernel_features {
 };
 
 bool btd_has_kernel_features(uint32_t feature);
+void btd_adapter_disconnect_all_devices(struct btd_adapter *adapter);
+void btd_adapter_refresh_is_blocked_by_policy(struct btd_adapter *adapter);
diff --git a/src/admin_policy.c b/src/admin_policy.c
new file mode 100644
index 000000000000..eeae2722cc0a
--- /dev/null
+++ b/src/admin_policy.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021 Google LLC
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "log.h"
+#include "metrics.h"
+#include "src/error.h"
+
+#include "admin_policy.h"
+#include "profile.h"
+
+#define ADMIN_POLICY_INTERFACE		"org.bluez.AdminPolicy1"
+
+struct btd_admin_policy {
+	struct btd_adapter *adapter;
+	uint16_t adapter_id;
+	GHashTable *allowed_uuid_set;	/* Set of allowed service uuids*/
+};
+
+static GHashTable *uuid_set_create(void)
+{
+	return g_hash_table_new_full(bt_uuid_hash, bt_uuid_equal, g_free, NULL);
+}
+
+static bool parse_allow_service_list(struct btd_adapter *adapter,
+					GHashTable **uuids,
+					DBusMessage *msg)
+{
+	DBusMessageIter iter, arriter;
+
+	dbus_message_iter_init(msg, &iter);
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return false;
+
+	*uuids = uuid_set_create();
+
+	if (!*uuids) {
+		error("Failed to create UUID allowed set");
+		return false;
+	}
+
+	dbus_message_iter_recurse(&iter, &arriter);
+	do {
+		const int type = dbus_message_iter_get_arg_type(&arriter);
+		char *uuid_param;
+		bt_uuid_t *uuid;
+
+		if (type == DBUS_TYPE_INVALID)
+			break;
+
+		if (type != DBUS_TYPE_STRING)
+			goto failed;
+
+		dbus_message_iter_get_basic(&arriter, &uuid_param);
+		uuid = g_try_malloc(sizeof(*uuid));
+
+		if (!uuid)
+			goto failed;
+
+		if (bt_string_to_uuid(uuid, uuid_param)) {
+			g_free(uuid);
+			goto failed;
+		}
+
+		g_hash_table_add(*uuids, uuid);
+		dbus_message_iter_next(&arriter);
+	} while (true);
+
+	return true;
+
+failed:
+	g_hash_table_destroy(*uuids);
+	*uuids = NULL;
+	return false;
+}
+
+GHashTable *btd_admin_policy_allowlist_get(
+					struct btd_admin_policy *admin_policy)
+{
+	return admin_policy ? admin_policy->allowed_uuid_set : NULL;
+}
+
+void btd_admin_policy_allowlist_set(struct btd_admin_policy *admin_policy,
+							GHashTable *uuids)
+{
+	if (!admin_policy)
+		return;
+
+	admin_policy->allowed_uuid_set = uuids;
+
+	/* This would add/remove profiles to the adapter according to the new
+	 * policy.
+	 */
+	btd_profile_policy_update(admin_policy->adapter);
+
+	/* Update auto-connect status to all devices */
+	btd_adapter_refresh_is_blocked_by_policy(admin_policy->adapter);
+}
+
+static DBusMessage *set_service_allowlist(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct btd_admin_policy *admin_policy = user_data;
+	struct btd_adapter *adapter = admin_policy->adapter;
+	GHashTable *uuid_set;
+	const char *sender = dbus_message_get_sender(msg);
+	bool connectable;
+
+	DBG("sender %s", sender);
+
+	/* Parse parameters */
+	if (!parse_allow_service_list(adapter, &uuid_set, msg)) {
+		btd_error(admin_policy->adapter_id,
+				"Failed on parsing allowed service list");
+		return btd_error_invalid_args(msg);
+	}
+
+	/* Diconnect all the existing connections in case some of them are not
+	 * allowed to use.
+	 */
+	btd_adapter_disconnect_all_devices(adapter);
+
+	/* Clear existing allowlist */
+	g_hash_table_destroy(admin_policy->allowed_uuid_set);
+
+	btd_admin_policy_allowlist_set(admin_policy, uuid_set);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable admin_policy_methods[] = {
+	{ GDBUS_EXPERIMENTAL_METHOD("SetServiceAllowList",
+					GDBUS_ARGS({ "UUIDs", "as" }),
+					NULL, set_service_allowlist) },
+	{ }
+};
+
+bool btd_admin_policy_uuid_is_allowed(struct btd_admin_policy *admin_policy,
+							const char *uuid_str)
+{
+	bt_uuid_t uuid;
+
+	if (!admin_policy)
+		return true;
+
+	if (bt_string_to_uuid(&uuid, uuid_str)) {
+		DBG("Failed to parse UUID string '%s'", uuid_str);
+		return false;
+	}
+
+	return !g_hash_table_size(admin_policy->allowed_uuid_set) ||
+		g_hash_table_contains(admin_policy->allowed_uuid_set, &uuid);
+}
+
+static struct btd_admin_policy *admin_policy_new(struct btd_adapter *adapter)
+{
+	struct btd_admin_policy *admin_policy;
+
+	admin_policy = g_try_malloc(sizeof(*admin_policy));
+
+	if (!admin_policy) {
+		error("Failed to allocate memory for admin_policy");
+		return NULL;
+	}
+
+	admin_policy->adapter = adapter;
+	admin_policy->adapter_id = btd_adapter_get_index(adapter);
+	admin_policy->allowed_uuid_set = uuid_set_create();
+
+	if (!admin_policy->allowed_uuid_set) {
+		error("Failed to create UUID allowed set");
+		g_free(admin_policy);
+		return NULL;
+	}
+
+	return admin_policy;
+}
+
+struct btd_admin_policy *btd_admin_policy_create(struct btd_adapter *adapter)
+{
+	struct btd_admin_policy *admin_policy;
+
+	admin_policy = admin_policy_new(adapter);
+	if (!admin_policy)
+		return NULL;
+
+	if (!g_dbus_register_interface(btd_get_dbus_connection(),
+					adapter_get_path(admin_policy->adapter),
+					ADMIN_POLICY_INTERFACE,
+					admin_policy_methods, NULL, NULL,
+					admin_policy, NULL)) {
+		btd_error(admin_policy->adapter_id,
+				"Failed to register "
+				ADMIN_POLICY_INTERFACE);
+		g_free(admin_policy);
+		return NULL;
+	}
+
+	DBG("register interface success!");
+	return admin_policy;
+}
+
+void btd_admin_policy_destroy(struct btd_admin_policy *admin_policy)
+{
+	if (!admin_policy)
+		return;
+
+	btd_info(admin_policy->adapter_id, "Destroy Admin Policy");
+
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+					adapter_get_path(admin_policy->adapter),
+					ADMIN_POLICY_INTERFACE);
+	g_hash_table_destroy(admin_policy->allowed_uuid_set);
+	g_free(admin_policy);
+}
diff --git a/src/admin_policy.h b/src/admin_policy.h
new file mode 100644
index 000000000000..5cb97f7ec81e
--- /dev/null
+++ b/src/admin_policy.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2021  Google LLC
+ *
+ *
+ */
+
+#include <glib.h>
+
+struct btd_adapter;
+struct btd_admin_policy;
+
+struct btd_admin_policy *btd_admin_policy_create(struct btd_adapter *adapter);
+void btd_admin_policy_destroy(struct btd_admin_policy *admin_policy);
+GHashTable *btd_admin_policy_allowlist_get(
+					struct btd_admin_policy *admin_policy);
+void btd_admin_policy_allowlist_set(struct btd_admin_policy *admin_policy,
+							GHashTable *uuids);
+bool btd_admin_policy_uuid_is_allowed(struct btd_admin_policy *admin_policy,
+							const char *uuid_str);
diff --git a/src/device.c b/src/device.c
index 7a6f7643afec..2192346d5f8d 100644
--- a/src/device.c
+++ b/src/device.c
@@ -1929,16 +1929,51 @@ static int service_prio_cmp(gconstpointer a, gconstpointer b)
 	return p2->priority - p1->priority;
 }
 
+void btd_device_update_is_blocked_by_policy(struct btd_device *dev)
+{
+	struct btd_adapter *adapter = dev->adapter;
+	struct btd_service *service;
+	struct btd_profile *profile;
+	GSList *l;
+	bool auto_connect = false;
+
+	/* If service discover is ongoing, let the service discover complete
+	 * callback call this function.
+	 */
+	if (dev->browse)
+		return;
+
+	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
+		service = l->data;
+		profile = btd_service_get_profile(service);
+
+		if (!profile->auto_connect)
+			continue;
+
+		if (profile->accept &&
+			!btd_service_is_blocked_by_policy(service))
+			auto_connect = true;
+	}
+
+	if (!dev->disable_auto_connect)
+		device_set_auto_connect(dev, auto_connect);
+}
+
 static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
 {
 	struct btd_service *service;
 	struct btd_profile *p;
 	GSList *l;
+	bool is_blocked;
 
 	if (uuid) {
 		service = find_connectable_service(dev, uuid);
-		if (service)
+		is_blocked = !btd_adapter_uuid_is_allowed(dev->adapter,	uuid);
+
+		if (service && !is_blocked)
 			return g_slist_prepend(dev->pending, service);
+		else if (is_blocked)
+			info("service %s is blocked by policy", uuid);
 
 		return dev->pending;
 	}
@@ -1946,10 +1981,17 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
 	for (l = dev->services; l != NULL; l = g_slist_next(l)) {
 		service = l->data;
 		p = btd_service_get_profile(service);
+		is_blocked = !btd_adapter_uuid_is_allowed(dev->adapter,
+								p->remote_uuid);
 
 		if (!p->auto_connect)
 			continue;
 
+		if (is_blocked) {
+			info("service %s is blocked by policy", p->remote_uuid);
+			continue;
+		}
+
 		if (g_slist_find(dev->pending, service))
 			continue;
 
@@ -2633,6 +2675,8 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t browse_type,
 							dev->svc_callbacks);
 		g_free(cb);
 	}
+
+	btd_device_update_is_blocked_by_policy(dev);
 }
 
 static struct bonding_req *bonding_request_new(DBusMessage *msg,
@@ -4627,9 +4671,10 @@ static struct btd_service *probe_service(struct btd_device *device,
 	}
 
 	/* Only set auto connect if profile has set the flag and can really
-	 * accept connections.
+	 * accept connections and is not blocked by policy.
 	 */
-	if (profile->auto_connect && profile->accept)
+	if (profile->auto_connect && profile->accept &&
+				!btd_service_is_blocked_by_policy(service))
 		device_set_auto_connect(device, TRUE);
 
 	return service;
diff --git a/src/device.h b/src/device.h
index 4ae9abe0dbb4..3ca4f2c56566 100644
--- a/src/device.h
+++ b/src/device.h
@@ -175,5 +175,6 @@ uint32_t btd_device_get_current_flags(struct btd_device *dev);
 void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags,
 			      uint32_t current_flags);
 
+void btd_device_update_is_blocked_by_policy(struct btd_device *dev);
 void btd_device_init(void);
 void btd_device_cleanup(void);
diff --git a/src/profile.c b/src/profile.c
index 5e460b639c19..ae21a079a4e3 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -2559,6 +2559,45 @@ bool btd_profile_remove_custom_prop(const char *uuid, const char *name)
 	return false;
 }
 
+static void update_profile_policy(struct btd_profile *p,
+						struct btd_adapter *adapter)
+{
+	bool is_blocked;
+
+	is_blocked = !btd_adapter_uuid_is_allowed(adapter, p->remote_uuid);
+
+	if (!is_blocked && p->is_blocked_by_policy) {
+		p->is_blocked_by_policy = false;
+		adapter_add_profile(adapter, p);
+
+		info("service %s is allowed by policy", p->remote_uuid);
+
+	} else if (is_blocked && !p->is_blocked_by_policy) {
+		p->is_blocked_by_policy = true;
+		adapter_remove_profile(adapter, p);
+
+		info("service %s is blocked by policy", p->remote_uuid);
+	}
+}
+
+void btd_profile_policy_update(struct btd_adapter *adapter)
+{
+	GSList *l;
+
+	for (l = profiles; l; l = l->next) {
+		struct btd_profile *p = l->data;
+
+		update_profile_policy(p, adapter);
+	}
+
+	for (l = ext_profiles; l; l = l->next) {
+		struct ext_profile *ext_p = l->data;
+		struct btd_profile *p = &ext_p->p;
+
+		update_profile_policy(p, adapter);
+	}
+}
+
 void btd_profile_init(void)
 {
 	g_dbus_register_interface(btd_get_dbus_connection(),
diff --git a/src/profile.h b/src/profile.h
index 6827f848148c..ddd4c94ec943 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -28,6 +28,8 @@ struct btd_profile {
 	 */
 	bool external;
 
+	bool is_blocked_by_policy;
+
 	int (*device_probe) (struct btd_service *service);
 	void (*device_remove) (struct btd_service *service);
 
@@ -40,6 +42,8 @@ struct btd_profile {
 						struct btd_adapter *adapter);
 	void (*adapter_remove) (struct btd_profile *p,
 						struct btd_adapter *adapter);
+
+	bool (*mandatory_services_are_allowed)(struct btd_service *service);
 };
 
 void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
@@ -63,6 +67,7 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type,
 					btd_profile_prop_get get,
 					void *user_data);
 bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
+void btd_profile_policy_update(struct btd_adapter *adapter);
 
 void btd_profile_init(void);
 void btd_profile_cleanup(void);
diff --git a/src/service.c b/src/service.c
index 21a52762e637..75e71549ae41 100644
--- a/src/service.c
+++ b/src/service.c
@@ -166,6 +166,20 @@ void service_remove(struct btd_service *service)
 	btd_service_unref(service);
 }
 
+bool btd_service_is_blocked_by_policy(struct btd_service *service)
+{
+	struct btd_adapter *adapter;
+
+	if (!service->profile->mandatory_services_are_allowed) {
+		adapter = device_get_adapter(service->device);
+
+		return !btd_adapter_uuid_is_allowed(adapter,
+						service->profile->remote_uuid);
+	}
+
+	return !service->profile->mandatory_services_are_allowed(service);
+}
+
 int service_accept(struct btd_service *service)
 {
 	char addr[18];
@@ -186,6 +200,13 @@ int service_accept(struct btd_service *service)
 	if (!service->profile->accept)
 		return -ENOSYS;
 
+	if (btd_service_is_blocked_by_policy(service)) {
+		info("service %s is blocked by policy",
+						service->profile->remote_uuid);
+
+		return -ECONNABORTED;
+	}
+
 	err = service->profile->accept(service);
 	if (!err)
 		goto done;
diff --git a/src/service.h b/src/service.h
index 88530cc17d53..e2960164fb17 100644
--- a/src/service.h
+++ b/src/service.h
@@ -35,6 +35,7 @@ struct btd_service *service_create(struct btd_device *device,
 int service_probe(struct btd_service *service);
 void service_remove(struct btd_service *service);
 
+bool btd_service_is_blocked_by_policy(struct btd_service *service);
 int service_accept(struct btd_service *service);
 int service_set_connecting(struct btd_service *service);
 
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 4/8] input/hog: block connection by policy
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (2 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 3/8] core: add AdminPolicy Interface Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 5/8] audio: Remove Media1 interface when a2dp source disallowed Howard Chung
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This blocks HoG connection if ServiceAllowList is set and some of the
mandatory services are not in the allowlist.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 profiles/input/hog.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index d50b823213b5..af4f73daf2b1 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -168,6 +168,27 @@ static void hog_remove(struct btd_service *service)
 	hog_device_free(dev);
 }
 
+static bool mandatory_services_are_allowed(struct btd_service *service)
+{
+	static const char * const mandatory_uuids[] = {DEVICE_INFORMATION_UUID,
+						BATTERY_UUID, HOG_UUID, NULL};
+	struct btd_device *device = btd_service_get_device(service);
+	struct btd_adapter *adapter = device_get_adapter(device);
+	struct btd_profile *p = btd_service_get_profile(service);
+
+	int i;
+
+	for (i = 0; mandatory_uuids[i] != NULL; i++) {
+		if (!btd_adapter_uuid_is_allowed(adapter, mandatory_uuids[i])) {
+			DBG("mandatory service %s is blocked by policy",
+							mandatory_uuids[i]);
+			return false;
+		}
+	}
+
+	return true;
+}
+
 static int hog_accept(struct btd_service *service)
 {
 	struct hog_device *dev = btd_service_get_user_data(service);
@@ -221,6 +242,7 @@ static struct btd_profile hog_profile = {
 	.accept		= hog_accept,
 	.disconnect	= hog_disconnect,
 	.auto_connect	= true,
+	.mandatory_services_are_allowed = mandatory_services_are_allowed,
 };
 
 static int hog_init(void)
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 5/8] audio: Remove Media1 interface when a2dp source disallowed
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (3 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 4/8] input/hog: block connection by policy Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 6/8] core: add properties IsBlockedByPolicy and ServiceAllowList Howard Chung
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou, Sonny Sasaka

When A2DP source profile is not allowed by policy, we remove the
a2dp_server and everything inside the object, which also release all
MediaEndpoints and MediaPlayer. When admin re-allowed A2DP source
profile, although we recreate the a2dp_server, clients are not able to
know they can register their endpoint and player now.

This patch handle this case by unregistering Media1 interface
when we remove a2dp_server, and register it back when a2dp_source is
allowed.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Reviewed-by: Sonny Sasaka <sonnysasaka@chromium.org>
---

 profiles/audio/a2dp.c  | 2 ++
 profiles/audio/avrcp.c | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index d31ed845cbe7..26d4f365207e 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -3275,6 +3275,7 @@ static int a2dp_source_server_probe(struct btd_profile *p,
 {
 	struct a2dp_server *server;
 
+	media_register(adapter);
 	DBG("path %s", adapter_get_path(adapter));
 
 	server = find_server(servers, adapter);
@@ -3315,6 +3316,7 @@ static void a2dp_source_server_remove(struct btd_profile *p,
 		return;
 
 	a2dp_server_unregister(server);
+	media_unregister(adapter);
 }
 
 static int a2dp_sink_server_probe(struct btd_profile *p,
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 05dd791ded7c..c25495f19492 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -4735,6 +4735,8 @@ static void avrcp_controller_server_remove(struct btd_profile *p,
 
 	if (server->tg_record_id == 0)
 		avrcp_server_unregister(server);
+
+	media_unregister(adapter);
 }
 
 static int avrcp_controller_server_probe(struct btd_profile *p,
@@ -4745,6 +4747,7 @@ static int avrcp_controller_server_probe(struct btd_profile *p,
 
 	DBG("path %s", adapter_get_path(adapter));
 
+	media_register(adapter);
 	server = find_server(servers, adapter);
 	if (server != NULL)
 		goto done;
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 6/8] core: add properties IsBlockedByPolicy and ServiceAllowList
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (4 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 5/8] audio: Remove Media1 interface when a2dp source disallowed Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 7/8] core: store ServiceAllowList into settings Howard Chung
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This adds IsBlockedByPolicy to indicate if the remote device is
currently blocked by policy.

ServiceAllowlist is also introduced to indicate the current service
allowlist.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 src/admin_policy.c | 57 +++++++++++++++++++++++++++++++++++++++++++---
 src/device.c       | 32 ++++++++++++++++++++++++--
 src/device.h       |  1 +
 3 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/src/admin_policy.c b/src/admin_policy.c
index eeae2722cc0a..6498dcf05319 100644
--- a/src/admin_policy.c
+++ b/src/admin_policy.c
@@ -118,8 +118,13 @@ void btd_admin_policy_allowlist_set(struct btd_admin_policy *admin_policy,
 	 */
 	btd_profile_policy_update(admin_policy->adapter);
 
-	/* Update auto-connect status to all devices */
+	/* Update auto-connect and IsBlockedByPolicy status to all devices */
 	btd_adapter_refresh_is_blocked_by_policy(admin_policy->adapter);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(),
+					adapter_get_path(admin_policy->adapter),
+					ADMIN_POLICY_INTERFACE,
+					"ServiceAllowList");
 }
 
 static DBusMessage *set_service_allowlist(DBusConnection *conn,
@@ -201,6 +206,51 @@ static struct btd_admin_policy *admin_policy_new(struct btd_adapter *adapter)
 	return admin_policy;
 }
 
+static void append_uuid_set(gpointer key, gpointer value, gpointer user_data)
+{
+	const bt_uuid_t *uuid = key;
+	DBusMessageIter *entry = user_data;
+	char uuid_str[MAX_LEN_UUID_STR];
+	const char *uuid_str_ptr = uuid_str;
+
+	bt_uuid_to_string(uuid, uuid_str, MAX_LEN_UUID_STR);
+	dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &uuid_str_ptr);
+}
+
+static gboolean
+property_get_service_allowlist(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct btd_admin_policy *admin_policy = user_data;
+	DBusMessageIter entry;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+					DBUS_TYPE_STRING_AS_STRING, &entry);
+
+	g_hash_table_foreach(admin_policy->allowed_uuid_set, append_uuid_set,
+									&entry);
+
+	dbus_message_iter_close_container(iter, &entry);
+
+	return TRUE;
+}
+
+static gboolean
+property_exists_service_allowlist(const GDBusPropertyTable *property,
+							void *user_data)
+{
+	struct btd_admin_policy *admin_policy = user_data;
+
+	return admin_policy->allowed_uuid_set != NULL ? TRUE : FALSE;
+}
+
+static const GDBusPropertyTable admin_policy_properties[] = {
+	{"ServiceAllowList", "as", property_get_service_allowlist, NULL,
+					property_exists_service_allowlist,
+					G_DBUS_PROPERTY_FLAG_EXPERIMENTAL},
+	{ }
+};
+
 struct btd_admin_policy *btd_admin_policy_create(struct btd_adapter *adapter)
 {
 	struct btd_admin_policy *admin_policy;
@@ -212,8 +262,9 @@ struct btd_admin_policy *btd_admin_policy_create(struct btd_adapter *adapter)
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					adapter_get_path(admin_policy->adapter),
 					ADMIN_POLICY_INTERFACE,
-					admin_policy_methods, NULL, NULL,
-					admin_policy, NULL)) {
+					admin_policy_methods, NULL,
+					admin_policy_properties, admin_policy,
+					NULL)) {
 		btd_error(admin_policy->adapter_id,
 				"Failed to register "
 				ADMIN_POLICY_INTERFACE);
diff --git a/src/device.c b/src/device.c
index 2192346d5f8d..aa54f00441c1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -265,6 +265,7 @@ struct btd_device {
 	gboolean	auto_connect;
 	gboolean	disable_auto_connect;
 	gboolean	general_connect;
+	gboolean	is_blocked_by_policy;
 
 	bool		legacy;
 	int8_t		rssi;
@@ -1470,6 +1471,18 @@ static gboolean dev_property_wake_allowed_exist(
 	return device_get_wake_support(device);
 }
 
+static gboolean
+dev_property_get_is_blocked_by_policy(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct btd_device *device = data;
+	dbus_bool_t is_blocked = device->is_blocked_by_policy;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &is_blocked);
+
+	return TRUE;
+}
+
 static bool disconnect_all(gpointer user_data)
 {
 	struct btd_device *device = user_data;
@@ -1929,6 +1942,16 @@ static int service_prio_cmp(gconstpointer a, gconstpointer b)
 	return p2->priority - p1->priority;
 }
 
+static void refresh_is_blocked_by_policy(struct btd_device *dev, bool value)
+{
+	if (value == dev->is_blocked_by_policy)
+		return;
+
+	dev->is_blocked_by_policy = value;
+	g_dbus_emit_property_changed(dbus_conn, dev->path,
+					DEVICE_INTERFACE, "IsBlockedByPolicy");
+}
+
 void btd_device_update_is_blocked_by_policy(struct btd_device *dev)
 {
 	struct btd_adapter *adapter = dev->adapter;
@@ -1936,6 +1959,7 @@ void btd_device_update_is_blocked_by_policy(struct btd_device *dev)
 	struct btd_profile *profile;
 	GSList *l;
 	bool auto_connect = false;
+	bool is_blocked = false;
 
 	/* If service discover is ongoing, let the service discover complete
 	 * callback call this function.
@@ -1950,13 +1974,16 @@ void btd_device_update_is_blocked_by_policy(struct btd_device *dev)
 		if (!profile->auto_connect)
 			continue;
 
-		if (profile->accept &&
-			!btd_service_is_blocked_by_policy(service))
+		if (btd_service_is_blocked_by_policy(service))
+			is_blocked = true;
+		else if (profile->accept)
 			auto_connect = true;
 	}
 
 	if (!dev->disable_auto_connect)
 		device_set_auto_connect(dev, auto_connect);
+
+	refresh_is_blocked_by_policy(dev, is_blocked);
 }
 
 static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
@@ -2994,6 +3021,7 @@ static const GDBusPropertyTable device_properties[] = {
 	{ "WakeAllowed", "b", dev_property_get_wake_allowed,
 				dev_property_set_wake_allowed,
 				dev_property_wake_allowed_exist },
+	{ "IsBlockedByPolicy", "b", dev_property_get_is_blocked_by_policy},
 	{ }
 };
 
diff --git a/src/device.h b/src/device.h
index 3ca4f2c56566..a24ae088b68e 100644
--- a/src/device.h
+++ b/src/device.h
@@ -74,6 +74,7 @@ void device_set_service_data(struct btd_device *dev, GSList *list,
 							bool duplicate);
 void device_set_data(struct btd_device *dev, GSList *list,
 							bool duplicate);
+void btd_device_update_is_blocked_by_policy(struct btd_device *dev);
 void device_probe_profile(gpointer a, gpointer b);
 void device_remove_profile(gpointer a, gpointer b);
 struct btd_adapter *device_get_adapter(struct btd_device *device);
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 7/8] core: store ServiceAllowList into settings
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (5 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 6/8] core: add properties IsBlockedByPolicy and ServiceAllowList Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 10:24 ` [Bluez PATCH 8/8] core: Initialize uuid_str_arr to NULL Howard Chung
  2021-04-01 17:08 ` [Bluez PATCH 0/8] Hi Linux-bluetooth, Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Howard Chung, Miao-chen Chou

This implements the logics of storing ServiceAllowList when the value
changed and restoring ServiceAllowList when adapter initialized.

The stored information would look like:
ServiceAllowList=$UUID128_STRING1;$UUID128_STRING2......

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
---

 src/adapter.c      | 103 +++++++++++++++++++++++++++++++++++++++++++++
 src/adapter.h      |   1 +
 src/admin_policy.c |   1 +
 3 files changed, 105 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 5531ed0e2246..f03eda2abc17 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -473,13 +473,19 @@ uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter)
 	return adapter->bdaddr_type;
 }
 
+static char **create_uuid_string_allowlist(GHashTable *allowed_uuid_set,
+							guint *num_of_uuid);
 static void store_adapter_info(struct btd_adapter *adapter)
 {
 	GKeyFile *key_file;
+	GHashTable *allowed_uuid_set;
 	char filename[PATH_MAX];
 	char *str;
 	gsize length = 0;
 	gboolean discoverable;
+	char **uuid_str_arr;
+	guint num_of_uuid = 0;
+	int i;
 
 	key_file = g_key_file_new();
 
@@ -508,15 +514,36 @@ static void store_adapter_info(struct btd_adapter *adapter)
 	snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings",
 					btd_adapter_get_storage_dir(adapter));
 
+	if (adapter->admin_policy) {
+		allowed_uuid_set = btd_admin_policy_allowlist_get(
+							adapter->admin_policy);
+		uuid_str_arr = create_uuid_string_allowlist(allowed_uuid_set,
+								&num_of_uuid);
+		g_key_file_set_string_list(key_file, "Policy",
+				"ServiceAllowList",
+				(const char *const*)uuid_str_arr, num_of_uuid);
+	}
 	create_file(filename, 0600);
 
+
 	str = g_key_file_to_data(key_file, &length, NULL);
 	g_file_set_contents(filename, str, length, NULL);
 	g_free(str);
 
+	for (i = 0; i < num_of_uuid; i++)
+		g_free(uuid_str_arr[i]);
+
+	g_free(uuid_str_arr);
+
 	g_key_file_free(key_file);
 }
 
+void btd_adapter_store_adapter_info(struct btd_adapter *adapter)
+{
+	if (adapter)
+		store_adapter_info(adapter);
+}
+
 static void trigger_pairable_timeout(struct btd_adapter *adapter);
 static void adapter_start(struct btd_adapter *adapter);
 static void adapter_stop(struct btd_adapter *adapter);
@@ -3509,6 +3536,32 @@ void btd_adapter_refresh_is_blocked_by_policy(struct btd_adapter *adapter)
 									NULL);
 }
 
+static char **create_uuid_string_allowlist(GHashTable *allowed_uuid_set,
+							guint *num_of_uuid)
+{
+	int i;
+	gpointer *uuids;
+	char **uuid_str_allowlist;
+	bt_uuid_t *uuid;
+
+	uuids = g_hash_table_get_keys_as_array(allowed_uuid_set, num_of_uuid);
+	uuid_str_allowlist = g_try_malloc_n(*num_of_uuid, sizeof(char *));
+
+	if (!uuid_str_allowlist && !num_of_uuid) {
+		error("Failed to allocate memory for uuid_str_allowlist");
+		return NULL;
+	}
+
+	for (i = 0; i < *num_of_uuid; i++) {
+		uuid = uuids[i];
+		uuid_str_allowlist[i] = g_try_malloc(MAX_LEN_UUID_STR);
+		bt_uuid_to_string(uuid, uuid_str_allowlist[i],
+							MAX_LEN_UUID_STR);
+	}
+
+	return uuid_str_allowlist;
+}
+
 static const GDBusMethodTable adapter_methods[] = {
 	{ GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
 	{ GDBUS_METHOD("SetDiscoveryFilter",
@@ -6265,12 +6318,42 @@ static void fix_storage(struct btd_adapter *adapter)
 	textfile_del(filename, "converted");
 }
 
+static GHashTable *uuid_list_to_uuid_set(gchar **uuids, gsize num_of_uuid)
+{
+	int i;
+	bt_uuid_t *uuid;
+	GHashTable *uuid_set = g_hash_table_new_full(bt_uuid_hash,
+						bt_uuid_equal, g_free, NULL);
+
+	if (!uuid_set)
+		return NULL;
+
+	for (i = 0; i < num_of_uuid; i++) {
+		uuid = g_try_malloc(sizeof(*uuid));
+
+		if (!uuid || bt_string_to_uuid(uuid, *uuids))
+			goto failed;
+
+		g_hash_table_add(uuid_set, uuid);
+		uuids++;
+	}
+
+	return uuid_set;
+
+failed:
+	g_hash_table_destroy(uuid_set);
+	return NULL;
+}
+
 static void load_config(struct btd_adapter *adapter)
 {
 	GKeyFile *key_file;
 	char filename[PATH_MAX];
 	struct stat st;
+	GHashTable *uuid_set;
 	GError *gerr = NULL;
+	gchar **uuids = NULL;
+	gsize num_of_uuid;
 
 	key_file = g_key_file_new();
 
@@ -6320,6 +6403,23 @@ static void load_config(struct btd_adapter *adapter)
 		gerr = NULL;
 	}
 
+	if (adapter->admin_policy) {
+		uuids = g_key_file_get_string_list(key_file, "Policy",
+				"ServiceAllowList", &num_of_uuid, &gerr);
+
+		if (gerr) {
+			uuids = NULL;
+			num_of_uuid = 0;
+			g_error_free(gerr);
+			gerr = NULL;
+		}
+
+		uuid_set = uuid_list_to_uuid_set(uuids, num_of_uuid);
+		if (uuid_set)
+			btd_admin_policy_allowlist_set(adapter->admin_policy,
+								uuid_set);
+	}
+
 	g_key_file_free(key_file);
 }
 
@@ -8761,6 +8861,9 @@ load:
 	load_defaults(adapter);
 	load_devices(adapter);
 
+	/* Update IsBlockedByPolicy on devices */
+	g_slist_foreach(adapter->devices, update_device_is_blocked_by_policy,
+									NULL);
 	/* restore Service Changed CCC value for bonded devices */
 	btd_gatt_database_restore_svc_chng_ccc(adapter->database);
 
diff --git a/src/adapter.h b/src/adapter.h
index 8e8c61b7bdf1..038754b6d76c 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -90,6 +90,7 @@ const char *adapter_get_path(struct btd_adapter *adapter);
 const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
 uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter);
 const char *btd_adapter_get_storage_dir(struct btd_adapter *adapter);
+void btd_adapter_store_adapter_info(struct btd_adapter *adapter);
 int adapter_set_name(struct btd_adapter *adapter, const char *name);
 
 int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec);
diff --git a/src/admin_policy.c b/src/admin_policy.c
index 6498dcf05319..0b51b379ed74 100644
--- a/src/admin_policy.c
+++ b/src/admin_policy.c
@@ -154,6 +154,7 @@ static DBusMessage *set_service_allowlist(DBusConnection *conn,
 	g_hash_table_destroy(admin_policy->allowed_uuid_set);
 
 	btd_admin_policy_allowlist_set(admin_policy, uuid_set);
+	btd_adapter_store_adapter_info(adapter);
 
 	return dbus_message_new_method_return(msg);
 }
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Bluez PATCH 8/8] core: Initialize uuid_str_arr to NULL
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (6 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 7/8] core: store ServiceAllowList into settings Howard Chung
@ 2021-04-01 10:24 ` Howard Chung
  2021-04-01 17:08 ` [Bluez PATCH 0/8] Hi Linux-bluetooth, Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Howard Chung @ 2021-04-01 10:24 UTC (permalink / raw)
  To: linux-bluetooth, luiz.dentz; +Cc: Sonny Sasaka, Daniel Winkler, Alain Michaud

From: Sonny Sasaka <sonnysasaka@chromium.org>

Not initializing may crash g_free() below.

Reviewed-by: Daniel Winkler <danielwinkler@google.com>
Reviewed-by: Alain Michaud <alainm@chromium.org>
---

 src/adapter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/adapter.c b/src/adapter.c
index f03eda2abc17..ab4f8a3ca4d7 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -483,7 +483,7 @@ static void store_adapter_info(struct btd_adapter *adapter)
 	char *str;
 	gsize length = 0;
 	gboolean discoverable;
-	char **uuid_str_arr;
+	char **uuid_str_arr = NULL;
 	guint num_of_uuid = 0;
 	int i;
 
-- 
2.31.0.291.g576ba9dcdaf-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Bluez PATCH 0/8] Hi Linux-bluetooth,
  2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
                   ` (7 preceding siblings ...)
  2021-04-01 10:24 ` [Bluez PATCH 8/8] core: Initialize uuid_str_arr to NULL Howard Chung
@ 2021-04-01 17:08 ` Luiz Augusto von Dentz
  8 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2021-04-01 17:08 UTC (permalink / raw)
  To: Howard Chung; +Cc: linux-bluetooth, Yun-Hao Chung

Hi Howard,

On Thu, Apr 1, 2021 at 3:25 AM Howard Chung <howardchung@google.com> wrote:
>
> From: Yun-Hao Chung <howardchung@chromium.org>
>
>
> This series is to add a new method |SetServiceAllowList| in D-Bus API to
> control what services can be connected by specifying UUID allow list.
> Users can query a device property |IsBlockedByPolicy| to tell if some
> of the services are currently restricted. Since we plan to add more
> methods to limit different bluetooth functionalities, we also introduce
> a new interface for this purpose. This series has been tested via sending
> commands with dbus-send manually on chromebook octopus and eve.
>
> Thanks,
> Howard
>
>
> Howard Chung (7):
>   doc: add description of SetServiceAllowList
>   lib: add hash functions for bt_uuid_t
>   core: add AdminPolicy Interface
>   input/hog: block connection by policy
>   audio: Remove Media1 interface when a2dp source disallowed
>   core: add properties IsBlockedByPolicy and ServiceAllowList
>   core: store ServiceAllowList into settings
>
> Sonny Sasaka (1):
>   core: Initialize uuid_str_arr to NULL
>
>  Makefile.am              |   3 +-
>  doc/admin_policy-api.txt |  34 +++++
>  doc/device-api.txt       |   7 +
>  lib/uuid.c               |  21 +++
>  lib/uuid.h               |   3 +
>  profiles/audio/a2dp.c    |   2 +
>  profiles/audio/avrcp.c   |   3 +
>  profiles/input/hog.c     |  22 +++
>  src/adapter.c            | 148 +++++++++++++++++++-
>  src/adapter.h            |   5 +
>  src/admin_policy.c       | 292 +++++++++++++++++++++++++++++++++++++++
>  src/admin_policy.h       |  23 +++
>  src/device.c             |  79 ++++++++++-
>  src/device.h             |   2 +
>  src/profile.c            |  39 ++++++
>  src/profile.h            |   5 +
>  src/service.c            |  21 +++
>  src/service.h            |   1 +
>  18 files changed, 705 insertions(+), 5 deletions(-)
>  create mode 100644 doc/admin_policy-api.txt
>  create mode 100644 src/admin_policy.c
>  create mode 100644 src/admin_policy.h
>
> --
> 2.31.0.291.g576ba9dcdaf-goog

I think I mentioned to one of you guys before so sorry I will be
repeating myself, we did in the past have control over
blocking/connection on a per service level using a plugin:

https://github.com/Vudentz/BlueZ/commits/service_api
https://github.com/Vudentz/BlueZ/blob/service_api/plugins/service.c

It probably need some rebasing to be applied on top but it already
gives upper layer control over things like auto-connect and blocked
properties, I've didn't add support for persisting the setting on
storage though since this use case was limited to car industry but if
we go into this direction I rather have this plugin instead.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-04-01 17:46 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-01 10:24 [Bluez PATCH 0/8] Hi Linux-bluetooth, Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 1/8] doc: add description of SetServiceAllowList Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 2/8] lib: add hash functions for bt_uuid_t Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 3/8] core: add AdminPolicy Interface Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 4/8] input/hog: block connection by policy Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 5/8] audio: Remove Media1 interface when a2dp source disallowed Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 6/8] core: add properties IsBlockedByPolicy and ServiceAllowList Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 7/8] core: store ServiceAllowList into settings Howard Chung
2021-04-01 10:24 ` [Bluez PATCH 8/8] core: Initialize uuid_str_arr to NULL Howard Chung
2021-04-01 17:08 ` [Bluez PATCH 0/8] Hi Linux-bluetooth, Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.