blob: cd9af6f955da19fed7233e695b2fbd32702c64a7 [file] [log] [blame]
/*
*
* 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 <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#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;
}