/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2021 Google LLC * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gdbus/gdbus.h" #include "src/shared/shell.h" #include "admin.h" #define _GNU_SOURCE static DBusConnection *dbus_conn; static GList *admin_proxies; static GDBusProxy *set_proxy; static GDBusProxy *status_proxy; static void admin_policy_set_set_proxy(GDBusProxy *proxy) { set_proxy = proxy; } static void admin_policy_set_status_proxy(GDBusProxy *proxy) { status_proxy = proxy; } static void admin_policy_read_service_allowlist(DBusConnection *dbus_conn) { DBusMessageIter iter, subiter; char *uuid = NULL; if (!status_proxy || !g_dbus_proxy_get_property(status_proxy, "ServiceAllowList", &iter)) { bt_shell_printf("Failed to get property\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { bt_shell_printf("Unexpected return type\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } bt_shell_printf("Service AllowedList:\n"); dbus_message_iter_recurse(&iter, &subiter); while (dbus_message_iter_get_arg_type(&subiter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&subiter, &uuid); bt_shell_printf("\t%s\n", uuid); dbus_message_iter_next(&subiter); } return bt_shell_noninteractive_quit(EXIT_SUCCESS); } struct uuid_list_data { char **uuid_list; size_t num; }; static void set_service_setup(DBusMessageIter *iter, void *user_data) { struct uuid_list_data *data = user_data; DBusMessageIter arr_iter; size_t i; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &arr_iter); for (i = 0; i < data->num; i++) { dbus_message_iter_append_basic(&arr_iter, DBUS_TYPE_STRING, &data->uuid_list[i]); } dbus_message_iter_close_container(iter, &arr_iter); } static void set_service_reply(DBusMessage *message, void *user_data) { DBusError error; dbus_error_init(&error); if (!dbus_set_error_from_message(&error, message)) { bt_shell_printf("Set allowed service successfully\n"); return bt_shell_noninteractive_quit(EXIT_SUCCESS); } bt_shell_printf("Failed to set service allowed list: %s\n", error.name); dbus_error_free(&error); return bt_shell_noninteractive_quit(EXIT_FAILURE); } static void admin_policy_set_service_allowlist(int argc, char *argv[]) { struct uuid_list_data data; if (!set_proxy) { bt_shell_printf("Set proxy not ready\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } data.uuid_list = argv; data.num = argc; if (!g_dbus_proxy_method_call(set_proxy, "SetServiceAllowList", set_service_setup, set_service_reply, &data, NULL)) { bt_shell_printf("Failed to call method\n"); return bt_shell_noninteractive_quit(EXIT_FAILURE); } } static void cmd_admin_allow(int argc, char *argv[]) { if (argc <= 1) { admin_policy_read_service_allowlist(dbus_conn); return; } if (strcmp(argv[1], "clear") == 0) argc--; admin_policy_set_service_allowlist(argc - 1, argv + 1); } static const struct bt_shell_menu admin_menu = { .name = "admin", .desc = "Admin Policy Submenu", .entries = { { "allow", "[clear/uuid1 uuid2 ...]", cmd_admin_allow, "Allow service UUIDs and block rest of them"}, {} }, }; static void admin_policy_status_added(GDBusProxy *proxy) { admin_proxies = g_list_append(admin_proxies, proxy); admin_policy_set_status_proxy(proxy); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.AdminPolicySet1")) admin_policy_set_set_proxy(proxy); else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) admin_policy_status_added(proxy); } static void admin_policy_status_removed(GDBusProxy *proxy) { admin_proxies = g_list_remove(admin_proxies, proxy); admin_policy_set_status_proxy(NULL); } static void proxy_removed(GDBusProxy *proxy, void *user_data) { const char *interface; interface = g_dbus_proxy_get_interface(proxy); if (!strcmp(interface, "org.bluez.AdminPolicySet1")) admin_policy_set_set_proxy(NULL); else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) admin_policy_status_removed(proxy); } static GDBusClient *client; static void disconnect_handler(DBusConnection *connection, void *user_data) { g_list_free_full(admin_proxies, NULL); admin_proxies = NULL; } void admin_add_submenu(void) { bt_shell_add_submenu(&admin_menu); dbus_conn = bt_shell_get_env("DBUS_CONNECTION"); if (!dbus_conn || client) return; client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, NULL, NULL); g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); } void admin_remove_submenu(void) { g_dbus_client_unref(client); client = NULL; }