| From 977321f2c7f974ea68a3d90df296c66189a3f254 Mon Sep 17 00:00:00 2001 |
| From: Lei Maohui <leimaohui@cn.fujitsu.com> |
| Date: Fri, 21 Jun 2019 17:57:35 +0900 |
| Subject: [PATCH] CVE-2018-10910 |
| |
| A bug in Bluez may allow for the Bluetooth Discoverable state being set to on |
| when no Bluetooth agent is registered with the system. This situation could |
| lead to the unauthorized pairing of certain Bluetooth devices without any |
| form of authentication. |
| |
| CVE: CVE-2018-10910 |
| Upstream-Status: Backport |
| Signed-off-by: Ross Burton <ross.burton@intel.com> |
| |
| Subject: [PATCH BlueZ 1/4] client: Add discoverable-timeout command |
| From: Luiz Augusto von Dentz <luiz.dentz () gmail ! com> |
| Date: 2018-07-25 10:20:32 |
| Message-ID: 20180725102035.19439-1-luiz.dentz () gmail ! com |
| [Download RAW message or body] |
| |
| From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> |
| |
| This adds discoverable-timeout command which can be used to get/set |
| DiscoverableTimeout property: |
| |
| [bluetooth]# discoverable-timeout 180 |
| Changing discoverable-timeout 180 succeeded |
| --- |
| client/main.c | 82 +++++++++++++++++++++++++++++++++- |
| doc/adapter-api.txt | 6 +++ |
| src/adapter.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++------ |
| 3 files changed, 198 insertions(+), 15 deletions(-) |
| |
| diff --git a/client/main.c b/client/main.c |
| index 87323d8..1a66a3a 100644 |
| --- a/client/main.c |
| +++ b/client/main.c |
| @@ -877,6 +877,7 @@ static void cmd_show(int argc, char *argv[]) |
| print_property(proxy, "Class"); |
| print_property(proxy, "Powered"); |
| print_property(proxy, "Discoverable"); |
| + print_property(proxy, "DiscoverableTimeout"); |
| print_property(proxy, "Pairable"); |
| print_uuids(proxy); |
| print_property(proxy, "Modalias"); |
| @@ -1061,6 +1062,47 @@ static void cmd_discoverable(int argc, char *argv[]) |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| +static void cmd_discoverable_timeout(int argc, char *argv[]) |
| +{ |
| + uint32_t value; |
| + char *endptr = NULL; |
| + char *str; |
| + |
| + if (argc < 2) { |
| + DBusMessageIter iter; |
| + |
| + if (!g_dbus_proxy_get_property(default_ctrl->proxy, |
| + "DiscoverableTimeout", &iter)) { |
| + bt_shell_printf("Unable to get DiscoverableTimeout\n"); |
| + return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| + } |
| + |
| + dbus_message_iter_get_basic(&iter, &value); |
| + |
| + bt_shell_printf("DiscoverableTimeout: %d seconds\n", value); |
| + |
| + return; |
| + } |
| + |
| + value = strtol(argv[1], &endptr, 0); |
| + if (!endptr || *endptr != '\0' || value > UINT32_MAX) { |
| + bt_shell_printf("Invalid argument\n"); |
| + return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| + } |
| + |
| + str = g_strdup_printf("discoverable-timeout %d", value); |
| + |
| + if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, |
| + "DiscoverableTimeout", |
| + DBUS_TYPE_UINT32, &value, |
| + generic_callback, str, g_free)) |
| + return; |
| + |
| + g_free(str); |
| + |
| + return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| +} |
| + |
| static void cmd_agent(int argc, char *argv[]) |
| { |
| dbus_bool_t enable; |
| @@ -1124,6 +1166,7 @@ static struct set_discovery_filter_args { |
| char **uuids; |
| size_t uuids_len; |
| dbus_bool_t duplicate; |
| + dbus_bool_t discoverable; |
| bool set; |
| } filter = { |
| .rssi = DISTANCE_VAL_INVALID, |
| @@ -1163,6 +1206,11 @@ static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data) |
| DBUS_TYPE_BOOLEAN, |
| &args->duplicate); |
| |
| + if (args->discoverable) |
| + g_dbus_dict_append_entry(&dict, "Discoverable", |
| + DBUS_TYPE_BOOLEAN, |
| + &args->discoverable); |
| + |
| dbus_message_iter_close_container(iter, &dict); |
| } |
| |
| @@ -1320,6 +1368,26 @@ static void cmd_scan_filter_duplicate_data(int argc, char *argv[]) |
| filter.set = false; |
| } |
| |
| +static void cmd_scan_filter_discoverable(int argc, char *argv[]) |
| +{ |
| + if (argc < 2 || !strlen(argv[1])) { |
| + bt_shell_printf("Discoverable: %s\n", |
| + filter.discoverable ? "on" : "off"); |
| + return bt_shell_noninteractive_quit(EXIT_SUCCESS); |
| + } |
| + |
| + if (!strcmp(argv[1], "on")) |
| + filter.discoverable = true; |
| + else if (!strcmp(argv[1], "off")) |
| + filter.discoverable = false; |
| + else { |
| + bt_shell_printf("Invalid option: %s\n", argv[1]); |
| + return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| + } |
| + |
| + filter.set = false; |
| +} |
| + |
| static void filter_clear_uuids(void) |
| { |
| g_strfreev(filter.uuids); |
| @@ -1348,6 +1416,11 @@ static void filter_clear_duplicate(void) |
| filter.duplicate = false; |
| } |
| |
| +static void filter_clear_discoverable(void) |
| +{ |
| + filter.discoverable = false; |
| +} |
| + |
| struct clear_entry { |
| const char *name; |
| void (*clear) (void); |
| @@ -1359,6 +1432,7 @@ static const struct clear_entry filter_clear[] = { |
| { "pathloss", filter_clear_pathloss }, |
| { "transport", filter_clear_transport }, |
| { "duplicate-data", filter_clear_duplicate }, |
| + { "discoverable", filter_clear_discoverable }, |
| {} |
| }; |
| |
| @@ -2468,7 +2542,11 @@ static const struct bt_shell_menu scan_menu = { |
| { "duplicate-data", "[on/off]", cmd_scan_filter_duplicate_data, |
| "Set/Get duplicate data filter", |
| NULL }, |
| - { "clear", "[uuids/rssi/pathloss/transport/duplicate-data]", |
| + { "discoverable", "[on/off]", cmd_scan_filter_discoverable, |
| + "Set/Get discoverable filter", |
| + NULL }, |
| + { "clear", |
| + "[uuids/rssi/pathloss/transport/duplicate-data/discoverable]", |
| cmd_scan_filter_clear, |
| "Clears discovery filter.", |
| filter_clear_generator }, |
| @@ -2549,6 +2627,8 @@ static const struct bt_shell_menu main_menu = { |
| { "discoverable", "<on/off>", cmd_discoverable, |
| "Set controller discoverable mode", |
| NULL }, |
| + { "discoverable-timeout", "[value]", cmd_discoverable_timeout, |
| + "Set discoverable timeout", NULL }, |
| { "agent", "<on/off/capability>", cmd_agent, |
| "Enable/disable agent with given capability", |
| capability_generator}, |
| diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt |
| index d14d0ca..4791af2 100644 |
| --- a/doc/adapter-api.txt |
| +++ b/doc/adapter-api.txt |
| @@ -113,6 +113,12 @@ Methods void StartDiscovery() |
| generated for either ManufacturerData and |
| ServiceData everytime they are discovered. |
| |
| + bool Discoverable (Default: false) |
| + |
| + Make adapter discoverable while discovering, |
| + if the adapter is already discoverable this |
| + setting this filter won't do anything. |
| + |
| When discovery filter is set, Device objects will be |
| created as new devices with matching criteria are |
| discovered regardless of they are connectable or |
| diff --git a/src/adapter.c b/src/adapter.c |
| index af340fd..822bd34 100644 |
| --- a/src/adapter.c |
| +++ b/src/adapter.c |
| @@ -157,6 +157,7 @@ struct discovery_filter { |
| int16_t rssi; |
| GSList *uuids; |
| bool duplicate; |
| + bool discoverable; |
| }; |
| |
| struct watch_client { |
| @@ -196,6 +197,7 @@ struct btd_adapter { |
| char *name; /* controller device name */ |
| char *short_name; /* controller short name */ |
| uint32_t supported_settings; /* controller supported settings */ |
| + uint32_t pending_settings; /* pending controller settings */ |
| uint32_t current_settings; /* current controller settings */ |
| |
| char *path; /* adapter object path */ |
| @@ -213,6 +215,7 @@ struct btd_adapter { |
| |
| bool discovering; /* discovering property state */ |
| bool filtered_discovery; /* we are doing filtered discovery */ |
| + bool filtered_discoverable; /* we are doing filtered discovery */ |
| bool no_scan_restart_delay; /* when this flag is set, restart scan |
| * without delay */ |
| uint8_t discovery_type; /* current active discovery type */ |
| @@ -509,8 +512,10 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings) |
| changed_mask = adapter->current_settings ^ settings; |
| |
| adapter->current_settings = settings; |
| + adapter->pending_settings &= ~changed_mask; |
| |
| DBG("Changed settings: 0x%08x", changed_mask); |
| + DBG("Pending settings: 0x%08x", adapter->pending_settings); |
| |
| if (changed_mask & MGMT_SETTING_POWERED) { |
| g_dbus_emit_property_changed(dbus_conn, adapter->path, |
| @@ -596,10 +601,31 @@ static bool set_mode(struct btd_adapter *adapter, uint16_t opcode, |
| uint8_t mode) |
| { |
| struct mgmt_mode cp; |
| + uint32_t setting = 0; |
| |
| memset(&cp, 0, sizeof(cp)); |
| cp.val = mode; |
| |
| + switch (mode) { |
| + case MGMT_OP_SET_POWERED: |
| + setting = MGMT_SETTING_POWERED; |
| + break; |
| + case MGMT_OP_SET_CONNECTABLE: |
| + setting = MGMT_SETTING_CONNECTABLE; |
| + break; |
| + case MGMT_OP_SET_FAST_CONNECTABLE: |
| + setting = MGMT_SETTING_FAST_CONNECTABLE; |
| + break; |
| + case MGMT_OP_SET_DISCOVERABLE: |
| + setting = MGMT_SETTING_DISCOVERABLE; |
| + break; |
| + case MGMT_OP_SET_BONDABLE: |
| + setting = MGMT_SETTING_DISCOVERABLE; |
| + break; |
| + } |
| + |
| + adapter->pending_settings |= setting; |
| + |
| DBG("sending set mode command for index %u", adapter->dev_id); |
| |
| if (mgmt_send(adapter->mgmt, opcode, |
| @@ -1818,7 +1844,17 @@ static void discovery_free(void *user_data) |
| g_free(client); |
| } |
| |
| -static void discovery_remove(struct watch_client *client) |
| +static bool set_filtered_discoverable(struct btd_adapter *adapter, bool enable) |
| +{ |
| + if (adapter->filtered_discoverable == enable) |
| + return true; |
| + |
| + adapter->filtered_discoverable = enable; |
| + |
| + return set_discoverable(adapter, enable, 0); |
| +} |
| + |
| +static void discovery_remove(struct watch_client *client, bool exit) |
| { |
| struct btd_adapter *adapter = client->adapter; |
| |
| @@ -1830,7 +1866,27 @@ static void discovery_remove(struct watch_client *client) |
| adapter->discovery_list = g_slist_remove(adapter->discovery_list, |
| client); |
| |
| - discovery_free(client); |
| + if (adapter->filtered_discoverable && |
| + client->discovery_filter->discoverable) { |
| + GSList *l; |
| + |
| + for (l = adapter->discovery_list; l; l = g_slist_next(l)) { |
| + struct watch_client *client = l->data; |
| + |
| + if (client->discovery_filter->discoverable) |
| + break; |
| + } |
| + |
| + /* Disable filtered discoverable if there are no clients */ |
| + if (!l) |
| + set_filtered_discoverable(adapter, false); |
| + } |
| + |
| + if (!exit && client->discovery_filter) |
| + adapter->set_filter_list = g_slist_prepend( |
| + adapter->set_filter_list, client); |
| + else |
| + discovery_free(client); |
| |
| /* |
| * If there are other client discoveries in progress, then leave |
| @@ -1859,8 +1915,11 @@ static void stop_discovery_complete(uint8_t status, uint16_t length, |
| goto done; |
| } |
| |
| - if (client->msg) |
| + if (client->msg) { |
| g_dbus_send_reply(dbus_conn, client->msg, DBUS_TYPE_INVALID); |
| + dbus_message_unref(client->msg); |
| + client->msg = NULL; |
| + } |
| |
| adapter->discovery_type = 0x00; |
| adapter->discovery_enable = 0x00; |
| @@ -1873,7 +1932,7 @@ static void stop_discovery_complete(uint8_t status, uint16_t length, |
| trigger_passive_scanning(adapter); |
| |
| done: |
| - discovery_remove(client); |
| + discovery_remove(client, false); |
| } |
| |
| static int compare_sender(gconstpointer a, gconstpointer b) |
| @@ -2094,14 +2153,14 @@ static int update_discovery_filter(struct btd_adapter *adapter) |
| return -EINPROGRESS; |
| } |
| |
| -static int discovery_stop(struct watch_client *client) |
| +static int discovery_stop(struct watch_client *client, bool exit) |
| { |
| struct btd_adapter *adapter = client->adapter; |
| struct mgmt_cp_stop_discovery cp; |
| |
| /* Check if there are more client discovering */ |
| if (g_slist_next(adapter->discovery_list)) { |
| - discovery_remove(client); |
| + discovery_remove(client, exit); |
| update_discovery_filter(adapter); |
| return 0; |
| } |
| @@ -2111,7 +2170,7 @@ static int discovery_stop(struct watch_client *client) |
| * and so it is enough to send out the signal and just return. |
| */ |
| if (adapter->discovery_enable == 0x00) { |
| - discovery_remove(client); |
| + discovery_remove(client, exit); |
| adapter->discovering = false; |
| g_dbus_emit_property_changed(dbus_conn, adapter->path, |
| ADAPTER_INTERFACE, "Discovering"); |
| @@ -2136,7 +2195,7 @@ static void discovery_disconnect(DBusConnection *conn, void *user_data) |
| |
| DBG("owner %s", client->owner); |
| |
| - discovery_stop(client); |
| + discovery_stop(client, true); |
| } |
| |
| /* |
| @@ -2200,6 +2259,15 @@ static DBusMessage *start_discovery(DBusConnection *conn, |
| adapter->set_filter_list, client); |
| adapter->discovery_list = g_slist_prepend( |
| adapter->discovery_list, client); |
| + |
| + /* Reset discoverable filter if already set */ |
| + if (adapter->current_settings & MGMT_OP_SET_DISCOVERABLE) |
| + goto done; |
| + |
| + /* Set discoverable if filter requires and it*/ |
| + if (client->discovery_filter->discoverable) |
| + set_filtered_discoverable(adapter, true); |
| + |
| goto done; |
| } |
| |
| @@ -2324,6 +2392,17 @@ static bool parse_duplicate_data(DBusMessageIter *value, |
| return true; |
| } |
| |
| +static bool parse_discoverable(DBusMessageIter *value, |
| + struct discovery_filter *filter) |
| +{ |
| + if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) |
| + return false; |
| + |
| + dbus_message_iter_get_basic(value, &filter->discoverable); |
| + |
| + return true; |
| +} |
| + |
| struct filter_parser { |
| const char *name; |
| bool (*func)(DBusMessageIter *iter, struct discovery_filter *filter); |
| @@ -2333,6 +2412,7 @@ struct filter_parser { |
| { "Pathloss", parse_pathloss }, |
| { "Transport", parse_transport }, |
| { "DuplicateData", parse_duplicate_data }, |
| + { "Discoverable", parse_discoverable }, |
| { } |
| }; |
| |
| @@ -2372,6 +2452,7 @@ static bool parse_discovery_filter_dict(struct btd_adapter *adapter, |
| (*filter)->rssi = DISTANCE_VAL_INVALID; |
| (*filter)->type = get_scan_type(adapter); |
| (*filter)->duplicate = false; |
| + (*filter)->discoverable = false; |
| |
| dbus_message_iter_init(msg, &iter); |
| if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || |
| @@ -2417,8 +2498,10 @@ static bool parse_discovery_filter_dict(struct btd_adapter *adapter, |
| goto invalid_args; |
| |
| DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d " |
| - " duplicate data: %s ", (*filter)->type, (*filter)->rssi, |
| - (*filter)->pathloss, (*filter)->duplicate ? "true" : "false"); |
| + " duplicate data: %s discoverable %s", (*filter)->type, |
| + (*filter)->rssi, (*filter)->pathloss, |
| + (*filter)->duplicate ? "true" : "false", |
| + (*filter)->discoverable ? "true" : "false"); |
| |
| return true; |
| |
| @@ -2510,7 +2593,7 @@ static DBusMessage *stop_discovery(DBusConnection *conn, |
| if (client->msg) |
| return btd_error_busy(msg); |
| |
| - err = discovery_stop(client); |
| + err = discovery_stop(client, false); |
| switch (err) { |
| case 0: |
| return dbus_message_new_method_return(msg); |
| @@ -2739,13 +2822,15 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, |
| else |
| current_enable = FALSE; |
| |
| - if (enable == current_enable) { |
| + if (enable == current_enable || adapter->pending_settings & setting) { |
| g_dbus_pending_property_success(id); |
| return; |
| } |
| |
| mode = (enable == TRUE) ? 0x01 : 0x00; |
| |
| + adapter->pending_settings |= setting; |
| + |
| switch (setting) { |
| case MGMT_SETTING_POWERED: |
| opcode = MGMT_OP_SET_POWERED; |
| @@ -2798,7 +2883,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, |
| data->id = id; |
| |
| if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param, |
| - property_set_mode_complete, data, g_free) > 0) |
| + property_set_mode_complete, data, g_free) > 0) |
| return; |
| |
| g_free(data); |
| @@ -2875,6 +2960,7 @@ static void property_set_discoverable_timeout( |
| GDBusPendingPropertySet id, void *user_data) |
| { |
| struct btd_adapter *adapter = user_data; |
| + bool enabled; |
| dbus_uint32_t value; |
| |
| dbus_message_iter_get_basic(iter, &value); |
| @@ -2888,8 +2974,19 @@ static void property_set_discoverable_timeout( |
| g_dbus_emit_property_changed(dbus_conn, adapter->path, |
| ADAPTER_INTERFACE, "DiscoverableTimeout"); |
| |
| + if (adapter->pending_settings & MGMT_SETTING_DISCOVERABLE) { |
| + if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) |
| + enabled = false; |
| + else |
| + enabled = true; |
| + } else { |
| + if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) |
| + enabled = true; |
| + else |
| + enabled = false; |
| + } |
| |
| - if (adapter->current_settings & MGMT_SETTING_DISCOVERABLE) |
| + if (enabled) |
| set_discoverable(adapter, 0x01, adapter->discoverable_timeout); |
| } |
| |
| -- |
| 2.7.4 |
| |