From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This makes use of DEFER_SETUP mechanism to do the following checks before accepting the connection: - Checks a valid device object exits - Checks if initiator/central as if the peripheral start connecting it may cause collisions. - Checks if the limit of allowed connections has been reached. --- src/device.c | 6 ++-- src/device.h | 1 + src/gatt-database.c | 88 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/device.c b/src/device.c index 652c03606b9e..77b38e97a7ea 100644 --- a/src/device.c +++ b/src/device.c @@ -310,7 +310,7 @@ static struct bearer_state *get_state(struct btd_device *dev, return &dev->le_state; } -static bool get_initiator(struct btd_device *dev) +bool btd_device_is_initiator(struct btd_device *dev) { if (dev->le_state.connected) return dev->le_state.initiator; @@ -3964,7 +3964,7 @@ done: } /* Notify driver about the new connection */ - service_accept(service, get_initiator(device)); + service_accept(service, btd_device_is_initiator(device)); } static void device_add_gatt_services(struct btd_device *device) @@ -3984,7 +3984,7 @@ static void device_add_gatt_services(struct btd_device *device) static void device_accept_gatt_profiles(struct btd_device *device) { GSList *l; - bool initiator = get_initiator(device); + bool initiator = btd_device_is_initiator(device); DBG("initiator %s", initiator ? "true" : "false"); diff --git a/src/device.h b/src/device.h index 96347ff229cc..3252e14eff84 100644 --- a/src/device.h +++ b/src/device.h @@ -67,6 +67,7 @@ GSList *btd_device_get_primaries(struct btd_device *device); struct gatt_db *btd_device_get_gatt_db(struct btd_device *device); struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device); struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device); +bool btd_device_is_initiator(struct btd_device *device); void *btd_device_get_attrib(struct btd_device *device); void btd_device_gatt_set_service_changed(struct btd_device *device, uint16_t start, uint16_t end); diff --git a/src/gatt-database.c b/src/gatt-database.c index 3b53bf2a3c84..01dd84e8e3f7 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -632,7 +632,6 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) struct btd_device *device; uint8_t dst_type; bdaddr_t src, dst; - uint16_t cid; if (gerr) { error("%s", gerr->message); @@ -642,7 +641,6 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST_TYPE, &dst_type, - BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (gerr) { error("bt_io_get: %s", gerr->message); @@ -657,21 +655,9 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) if (!adapter) return; - /* Check cid before attempting to create device, if the device is using - * an RPA it could be that the MGMT event has not been processed yet - * which would lead to create a second copy of the same device using its - * identity address. - */ - if (cid == BT_ATT_CID) - device = btd_adapter_get_device(adapter, &dst, dst_type); - else - device = btd_adapter_find_device(adapter, &dst, dst_type); - - if (!device) { - error("Unable to find device, dropping connection attempt"); - g_io_channel_shutdown(io, FALSE, NULL); + device = btd_adapter_get_device(adapter, &dst, dst_type); + if (!device) return; - } device_attach_att(device, io); } @@ -3802,6 +3788,70 @@ static uint8_t server_authorize(struct bt_att *att, uint8_t opcode, return BT_ATT_ERROR_DB_OUT_OF_SYNC; } +static void eatt_confirm_cb(GIOChannel *io, gpointer data) +{ + char address[18]; + uint8_t dst_type; + bdaddr_t src, dst; + GError *gerr = NULL; + struct btd_device *device; + struct bt_gatt_server *server; + struct bt_att *att; + + bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_DEST, address, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + goto drop; + } + + DBG("New incoming EATT connection"); + + /* Confirm the device exists before accepting the connection, if the + * device is using an RPA it could be that the MGMT event has not been + * processed yet which would lead to create a second copy of the same + * device using its identity address. + */ + device = btd_adapter_find_device(adapter_find(&src), &dst, dst_type); + if (!device) { + error("Unable to find device: %s", address); + goto drop; + } + + /* Only allow EATT connection from central */ + if (btd_device_is_initiator(device)) { + warn("EATT connection from peripheral may cause collisions"); + goto drop; + } + + server = btd_device_get_gatt_server(device); + if (!server) { + error("Unable to resolve bt_server"); + goto drop; + } + + att = bt_gatt_server_get_att(server); + if (bt_att_get_channels(att) == btd_opts.gatt_channels) { + DBG("EATT channel limit reached"); + goto drop; + } + + if (!bt_io_accept(io, connect_cb, NULL, NULL, &gerr)) { + error("bt_io_accept: %s", gerr->message); + g_error_free(gerr); + goto drop; + } + + return; + +drop: + g_io_channel_shutdown(io, TRUE, NULL); +} + struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) { struct btd_gatt_database *database; @@ -3838,14 +3888,14 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) if (btd_opts.gatt_channels == 1) goto bredr; - /* EATT socket */ - database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, + /* EATT socket, encryption is required */ + database->eatt_io = bt_io_listen(NULL, eatt_confirm_cb, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), BT_IO_OPT_PSM, BT_ATT_EATT_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_MTU, btd_opts.gatt_mtu, BT_IO_OPT_INVALID); if (!database->eatt_io) { -- 2.39.2