| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- // 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
- #include <stdlib.h>
- #include <dbus/dbus.h>
- #include <gdbus/gdbus.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include "lib/bluetooth.h"
- #include "lib/uuid.h"
- #include "src/adapter.h"
- #include "src/dbus-common.h"
- #include "src/device.h"
- #include "src/error.h"
- #include "src/log.h"
- #include "src/plugin.h"
- #include "src/textfile.h"
- #include "src/shared/queue.h"
- #define ADMIN_POLICY_SET_INTERFACE "org.bluez.AdminPolicySet1"
- #define ADMIN_POLICY_STATUS_INTERFACE "org.bluez.AdminPolicyStatus1"
- #define ADMIN_POLICY_STORAGE STORAGEDIR "/admin_policy_settings"
- #define DBUS_BLUEZ_SERVICE "org.bluez"
- #define BTD_DEVICE_INTERFACE "org.bluez.Device1"
- static DBusConnection *dbus_conn;
- static struct queue *devices; /* List of struct device_data objects */
- /* |policy_data| has the same life cycle as btd_adapter */
- static struct btd_admin_policy {
- struct btd_adapter *adapter;
- uint16_t adapter_id;
- struct queue *service_allowlist;
- } *policy_data = NULL;
- struct device_data {
- struct btd_device *device;
- char *path;
- bool affected;
- };
- static struct btd_admin_policy *admin_policy_new(struct btd_adapter *adapter)
- {
- struct btd_admin_policy *admin_policy = NULL;
- admin_policy = g_try_malloc(sizeof(*admin_policy));
- if (!admin_policy) {
- btd_error(btd_adapter_get_index(adapter),
- "Failed to allocate memory for admin_policy");
- return NULL;
- }
- admin_policy->adapter = adapter;
- admin_policy->adapter_id = btd_adapter_get_index(adapter);
- admin_policy->service_allowlist = queue_new();
- return admin_policy;
- }
- static void free_service_allowlist(struct queue *q)
- {
- queue_destroy(q, free);
- }
- static void admin_policy_free(void *data)
- {
- struct btd_admin_policy *admin_policy = data;
- free_service_allowlist(admin_policy->service_allowlist);
- g_free(admin_policy);
- }
- static void admin_policy_destroy(struct btd_admin_policy *admin_policy)
- {
- const char *path = adapter_get_path(admin_policy->adapter);
- g_dbus_unregister_interface(dbus_conn, path,
- ADMIN_POLICY_SET_INTERFACE);
- g_dbus_unregister_interface(dbus_conn, path,
- ADMIN_POLICY_STATUS_INTERFACE);
- admin_policy_free(admin_policy);
- }
- static bool uuid_match(const void *data, const void *match_data)
- {
- const bt_uuid_t *uuid = data;
- const bt_uuid_t *match_uuid = match_data;
- return bt_uuid_cmp(uuid, match_uuid) == 0;
- }
- static struct queue *parse_allow_service_list(struct btd_adapter *adapter,
- DBusMessage *msg)
- {
- DBusMessageIter iter, arr_iter;
- struct queue *uuid_list = NULL;
- dbus_message_iter_init(msg, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
- return NULL;
- uuid_list = queue_new();
- dbus_message_iter_recurse(&iter, &arr_iter);
- do {
- const int type = dbus_message_iter_get_arg_type(&arr_iter);
- 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(&arr_iter, &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;
- }
- dbus_message_iter_next(&arr_iter);
- if (queue_find(uuid_list, uuid_match, uuid)) {
- g_free(uuid);
- continue;
- }
- queue_push_head(uuid_list, uuid);
- } while (true);
- return uuid_list;
- failed:
- queue_destroy(uuid_list, g_free);
- return NULL;
- }
- static bool service_allowlist_set(struct btd_admin_policy *admin_policy,
- struct queue *uuid_list)
- {
- struct btd_adapter *adapter = admin_policy->adapter;
- if (!btd_adapter_set_allowed_uuids(adapter, uuid_list))
- return false;
- free_service_allowlist(admin_policy->service_allowlist);
- admin_policy->service_allowlist = uuid_list;
- return true;
- }
- static void update_device_affected(void *data, void *user_data)
- {
- struct device_data *dev_data = data;
- bool affected;
- if (!dev_data) {
- error("Unexpected NULL device_data when updating device");
- return;
- }
- affected = !btd_device_all_services_allowed(dev_data->device);
- if (affected == dev_data->affected)
- return;
- dev_data->affected = affected;
- g_dbus_emit_property_changed(dbus_conn, dev_data->path,
- ADMIN_POLICY_STATUS_INTERFACE, "AffectedByPolicy");
- }
- static void free_uuid_strings(char **uuid_strs, gsize num)
- {
- gsize i;
- for (i = 0; i < num; i++)
- g_free(uuid_strs[i]);
- g_free(uuid_strs);
- }
- static char **new_uuid_strings(struct queue *allowlist, gsize *num)
- {
- const struct queue_entry *entry = NULL;
- bt_uuid_t *uuid = NULL;
- char **uuid_strs = NULL;
- gsize i = 0, allowlist_num;
- allowlist_num = queue_length(allowlist);
- if (!allowlist_num) {
- *num = 0;
- return NULL;
- }
- /* Set num to a non-zero number so that whoever call this could know if
- * this function success or not
- */
- *num = 1;
- uuid_strs = g_try_malloc_n(allowlist_num, sizeof(char *));
- if (!uuid_strs)
- return NULL;
- for (entry = queue_get_entries(allowlist); entry != NULL;
- entry = entry->next) {
- uuid = entry->data;
- uuid_strs[i] = g_try_malloc0(MAX_LEN_UUID_STR * sizeof(char));
- if (!uuid_strs[i])
- goto failed;
- bt_uuid_to_string(uuid, uuid_strs[i], MAX_LEN_UUID_STR);
- i++;
- }
- *num = allowlist_num;
- return uuid_strs;
- failed:
- free_uuid_strings(uuid_strs, i);
- return NULL;
- }
- static void store_policy_settings(struct btd_admin_policy *admin_policy)
- {
- GKeyFile *key_file = NULL;
- char *filename = ADMIN_POLICY_STORAGE;
- char *key_file_data = NULL;
- char **uuid_strs = NULL;
- gsize length, num_uuids;
- key_file = g_key_file_new();
- uuid_strs = new_uuid_strings(admin_policy->service_allowlist,
- &num_uuids);
- if (!uuid_strs && num_uuids) {
- btd_error(admin_policy->adapter_id,
- "Failed to allocate uuid strings");
- goto failed;
- }
- g_key_file_set_string_list(key_file, "General", "ServiceAllowlist",
- (const gchar * const *)uuid_strs,
- num_uuids);
- if (create_file(ADMIN_POLICY_STORAGE, 0600) < 0) {
- btd_error(admin_policy->adapter_id, "create %s failed, %s",
- filename, strerror(errno));
- goto failed;
- }
- key_file_data = g_key_file_to_data(key_file, &length, NULL);
- g_file_set_contents(ADMIN_POLICY_STORAGE, key_file_data, length, NULL);
- g_free(key_file_data);
- free_uuid_strings(uuid_strs, num_uuids);
- failed:
- g_key_file_free(key_file);
- }
- static void key_file_load_service_allowlist(GKeyFile *key_file,
- struct btd_admin_policy *admin_policy)
- {
- GError *gerr = NULL;
- struct queue *uuid_list = NULL;
- gchar **uuids = NULL;
- gsize num, i;
- uuids = g_key_file_get_string_list(key_file, "General",
- "ServiceAllowlist", &num, &gerr);
- if (gerr) {
- btd_error(admin_policy->adapter_id,
- "Failed to load ServiceAllowlist");
- g_error_free(gerr);
- return;
- }
- uuid_list = queue_new();
- for (i = 0; i < num; i++) {
- bt_uuid_t *uuid = g_try_malloc(sizeof(*uuid));
- if (!uuid)
- goto failed;
- if (bt_string_to_uuid(uuid, uuids[i])) {
- btd_error(admin_policy->adapter_id,
- "Failed to convert '%s' to uuid struct",
- *uuids);
- g_free(uuid);
- goto failed;
- }
- queue_push_tail(uuid_list, uuid);
- }
- if (!service_allowlist_set(admin_policy, uuid_list))
- goto failed;
- g_strfreev(uuids);
- return;
- failed:
- g_strfreev(uuids);
- free_service_allowlist(uuid_list);
- }
- static void load_policy_settings(struct btd_admin_policy *admin_policy)
- {
- GKeyFile *key_file;
- char *filename = ADMIN_POLICY_STORAGE;
- struct stat st;
- if (stat(filename, &st) < 0)
- store_policy_settings(policy_data);
- key_file = g_key_file_new();
- g_key_file_load_from_file(key_file, filename, 0, NULL);
- key_file_load_service_allowlist(key_file, admin_policy);
- g_key_file_free(key_file);
- }
- 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;
- struct queue *uuid_list = NULL;
- const char *sender = dbus_message_get_sender(msg);
- DBG("sender %s", sender);
- /* Parse parameters */
- uuid_list = parse_allow_service_list(adapter, msg);
- if (!uuid_list) {
- btd_error(admin_policy->adapter_id,
- "Failed on parsing allowed service list");
- return btd_error_invalid_args(msg);
- }
- if (service_allowlist_set(admin_policy, uuid_list)) {
- store_policy_settings(admin_policy);
- } else {
- free_service_allowlist(uuid_list);
- return btd_error_failed(msg, "service_allowlist_set failed");
- }
- g_dbus_emit_property_changed(dbus_conn,
- adapter_get_path(policy_data->adapter),
- ADMIN_POLICY_STATUS_INTERFACE,
- "ServiceAllowList");
- queue_foreach(devices, update_device_affected, NULL);
- return dbus_message_new_method_return(msg);
- }
- static const GDBusMethodTable admin_policy_adapter_methods[] = {
- { GDBUS_METHOD("SetServiceAllowList", GDBUS_ARGS({ "UUIDs", "as" }),
- NULL, set_service_allowlist) },
- { }
- };
- static void append_service_uuid(void *data, void *user_data)
- {
- bt_uuid_t *uuid = data;
- DBusMessageIter *entry = user_data;
- char uuid_str[MAX_LEN_UUID_STR];
- const char *uuid_str_ptr = uuid_str;
- if (!uuid) {
- error("Unexpected NULL uuid data in service_allowlist");
- return;
- }
- 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);
- queue_foreach(admin_policy->service_allowlist, append_service_uuid,
- &entry);
- dbus_message_iter_close_container(iter, &entry);
- return TRUE;
- }
- static const GDBusPropertyTable admin_policy_adapter_properties[] = {
- { "ServiceAllowList", "as", property_get_service_allowlist },
- { }
- };
- static bool device_data_match(const void *a, const void *b)
- {
- const struct device_data *data = a;
- const struct btd_device *dev = b;
- if (!data) {
- error("Unexpected NULL device_data");
- return false;
- }
- return data->device == dev;
- }
- static gboolean property_get_affected_by_policy(
- const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
- {
- struct device_data *data = user_data;
- if (!data) {
- error("Unexpected error: device_data is NULL");
- return FALSE;
- }
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
- &data->affected);
- return TRUE;
- }
- static const GDBusPropertyTable admin_policy_device_properties[] = {
- { "AffectedByPolicy", "b", property_get_affected_by_policy },
- { }
- };
- static void free_device_data(void *data)
- {
- struct device_data *device_data = data;
- g_free(device_data->path);
- g_free(device_data);
- }
- static void remove_device_data(void *data)
- {
- struct device_data *device_data = data;
- DBG("device_data for %s removing", device_data->path);
- queue_remove(devices, device_data);
- free_device_data(device_data);
- }
- static int admin_policy_adapter_probe(struct btd_adapter *adapter)
- {
- const char *adapter_path;
- if (policy_data) {
- btd_warn(policy_data->adapter_id,
- "Policy data already exists");
- admin_policy_free(policy_data);
- policy_data = NULL;
- }
- policy_data = admin_policy_new(adapter);
- if (!policy_data)
- return -ENOMEM;
- load_policy_settings(policy_data);
- adapter_path = adapter_get_path(adapter);
- if (!g_dbus_register_interface(dbus_conn, adapter_path,
- ADMIN_POLICY_SET_INTERFACE,
- admin_policy_adapter_methods, NULL,
- NULL, policy_data, NULL)) {
- btd_error(policy_data->adapter_id,
- "Admin Policy Set interface init failed on path %s",
- adapter_path);
- return -EINVAL;
- }
- btd_info(policy_data->adapter_id,
- "Admin Policy Set interface registered");
- if (!g_dbus_register_interface(dbus_conn, adapter_path,
- ADMIN_POLICY_STATUS_INTERFACE,
- NULL, NULL,
- admin_policy_adapter_properties,
- policy_data, NULL)) {
- btd_error(policy_data->adapter_id,
- "Admin Policy Status interface init failed on path %s",
- adapter_path);
- return -EINVAL;
- }
- btd_info(policy_data->adapter_id,
- "Admin Policy Status interface registered");
- return 0;
- }
- static void admin_policy_device_added(struct btd_adapter *adapter,
- struct btd_device *device)
- {
- struct device_data *data;
- if (queue_find(devices, device_data_match, device))
- return;
- data = g_new0(struct device_data, 1);
- if (!data) {
- btd_error(btd_adapter_get_index(adapter),
- "Failed to allocate memory for device_data");
- return;
- }
- data->device = device;
- data->path = g_strdup(device_get_path(device));
- data->affected = !btd_device_all_services_allowed(data->device);
- if (!g_dbus_register_interface(dbus_conn, data->path,
- ADMIN_POLICY_STATUS_INTERFACE,
- NULL, NULL,
- admin_policy_device_properties,
- data, remove_device_data)) {
- btd_error(btd_adapter_get_index(adapter),
- "Admin Policy Status interface init failed on path %s",
- device_get_path(device));
- free_device_data(data);
- return;
- }
- queue_push_tail(devices, data);
- DBG("device_data for %s added", data->path);
- }
- static void unregister_device_data(void *data, void *user_data)
- {
- struct device_data *dev_data = data;
- g_dbus_unregister_interface(dbus_conn, dev_data->path,
- ADMIN_POLICY_STATUS_INTERFACE);
- }
- static void admin_policy_device_removed(struct btd_adapter *adapter,
- struct btd_device *device)
- {
- struct device_data *data;
- data = queue_find(devices, device_data_match, device);
- if (data)
- unregister_device_data(data, NULL);
- }
- static void admin_policy_remove(struct btd_adapter *adapter)
- {
- DBG("");
- queue_foreach(devices, unregister_device_data, NULL);
- queue_destroy(devices, g_free);
- devices = NULL;
- if (policy_data) {
- admin_policy_destroy(policy_data);
- policy_data = NULL;
- }
- }
- static struct btd_adapter_driver admin_policy_driver = {
- .name = "admin_policy",
- .probe = admin_policy_adapter_probe,
- .resume = NULL,
- .remove = admin_policy_remove,
- .device_resolved = admin_policy_device_added,
- .device_removed = admin_policy_device_removed
- };
- static int admin_init(void)
- {
- DBG("");
- dbus_conn = btd_get_dbus_connection();
- devices = queue_new();
- return btd_register_adapter_driver(&admin_policy_driver);
- }
- static void admin_exit(void)
- {
- DBG("");
- btd_unregister_adapter_driver(&admin_policy_driver);
- }
- BLUETOOTH_PLUGIN_DEFINE(admin, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- admin_init, admin_exit)
|