[PATCH 3/5] android/gatt: Fix write commands being not handled

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Registering for GATTRIB_ALL_REQS now means only requests and not commands
or other type of att operations. Those needs their own handlers to be
registered.
---
 android/gatt.c | 475 +++++++++++++++++++++++++++++----------------------------
 1 file changed, 245 insertions(+), 230 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index cb87810..766aae9 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -161,7 +161,9 @@ struct gatt_device {
 	bool partial_srvc_search;
 
 	guint watch_id;
-	guint server_id;
+	guint req_handler_id;
+	guint write_cmd_handler_id;
+	guint sign_write_cmd_handler_id;
 
 	int ref;
 	int conn_cnt;
@@ -630,8 +632,15 @@ static void connection_cleanup(struct gatt_device *device)
 	if (device->attrib) {
 		GAttrib *attrib = device->attrib;
 
-		if (device->server_id > 0)
-			g_attrib_unregister(device->attrib, device->server_id);
+		if (device->req_handler_id > 0)
+			g_attrib_unregister(device->attrib,
+							device->req_handler_id);
+		if (device->write_cmd_handler_id > 0)
+			g_attrib_unregister(device->attrib,
+						device->write_cmd_handler_id);
+		if (device->sign_write_cmd_handler_id > 0)
+			g_attrib_unregister(device->attrib,
+					device->sign_write_cmd_handler_id);
 
 		device->attrib = NULL;
 		g_attrib_cancel_all(attrib);
@@ -943,7 +952,8 @@ static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
 	return FALSE;
 }
 
-static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+static void att_req_handler(const uint8_t *ipdu, uint16_t len,
+							gpointer user_data);
 
 static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
 							gpointer user_data)
@@ -1431,6 +1441,222 @@ static void create_app_connection(void *data, void *user_data)
 		create_connection(dev, app);
 }
 
+static void write_confirm(struct gatt_db_attribute *attrib,
+						int err, void *user_data)
+{
+	if (!err)
+		return;
+
+	error("Error writting attribute %p", attrib);
+}
+
+static uint8_t check_device_permissions(struct gatt_device *device,
+					uint8_t opcode, uint32_t permissions)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(device->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID))
+		return ATT_ECODE_UNLIKELY;
+
+	DBG("opcode %u permissions %u sec_level %u", opcode, permissions,
+								sec_level);
+
+	switch (opcode) {
+	case ATT_OP_SIGNED_WRITE_CMD:
+		if (!(permissions & GATT_PERM_WRITE_SIGNED))
+				return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+		break;
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_FIND_BY_TYPE_REQ:
+	case ATT_OP_FIND_INFO_REQ:
+		if (!(permissions & GATT_PERM_READ))
+			return ATT_ECODE_READ_NOT_PERM;
+
+		if ((permissions & GATT_PERM_READ_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_READ_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+		if (!(permissions & GATT_PERM_WRITE))
+			return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((permissions & GATT_PERM_WRITE_MITM) &&
+						sec_level < BT_SECURITY_HIGH)
+			return ATT_ECODE_AUTHENTICATION;
+
+		if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
+						sec_level < BT_SECURITY_MEDIUM)
+			return ATT_ECODE_INSUFF_ENC;
+
+		if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
+			return ATT_ECODE_AUTHORIZATION;
+		break;
+	default:
+		return ATT_ECODE_UNLIKELY;
+	}
+
+	return 0;
+}
+
+static int get_cid(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	uint16_t cid;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get CID");
+		return -1;
+	}
+
+	return cid;
+}
+
+static int get_sec_level(struct gatt_device *dev)
+{
+	GIOChannel *io;
+	int sec_level;
+
+	io = g_attrib_get_channel(dev->attrib);
+
+	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							BT_IO_OPT_INVALID)) {
+		error("gatt: Failed to get sec_level");
+		return -1;
+	}
+
+	return sec_level;
+}
+
+static void att_write_cmd_handler(const uint8_t *cmd, uint16_t cmd_len,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint8_t value[cmd_len];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+
+	len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+	if (!len)
+		return;
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	if (!gatt_db_attribute_get_permissions(attrib, &permissions))
+		return;
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr,
+							write_confirm, NULL);
+}
+
+static void att_sign_write_cmd_handler(const uint8_t *cmd, uint16_t cmd_len,
+							gpointer user_data)
+{
+	struct gatt_device *dev = user_data;
+	uint8_t value[cmd_len];
+	uint8_t s[ATT_SIGNATURE_LEN];
+	struct gatt_db_attribute *attrib;
+	uint32_t permissions;
+	uint16_t handle;
+	uint16_t len;
+	size_t vlen;
+	uint8_t csrk[16];
+	uint32_t sign_cnt;
+
+	if (get_cid(dev) != ATT_CID) {
+		error("gatt: Remote tries write signed on BR/EDR bearer");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (get_sec_level(dev) != BT_SECURITY_LOW) {
+		error("gatt: Remote tries write signed on encrypted link");
+		connection_cleanup(dev);
+		return;
+	}
+
+	if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) {
+		error("gatt: No valid csrk from remote device");
+		return;
+	}
+
+	len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
+
+	if (handle == 0)
+		return;
+
+	attrib = gatt_db_get_attribute(gatt_db, handle);
+	if (!attrib)
+		return;
+
+	gatt_db_attribute_get_permissions(attrib, &permissions);
+
+	if (check_device_permissions(dev, cmd[0], permissions))
+		return;
+
+	if (len) {
+		uint8_t t[ATT_SIGNATURE_LEN];
+		uint32_t r_sign_cnt = get_le32(s);
+
+		if (r_sign_cnt < sign_cnt) {
+			error("gatt: Invalid sign counter (%d<%d)",
+							r_sign_cnt, sign_cnt);
+			return;
+		}
+
+		/* Generate signature and verify it */
+		if (!bt_crypto_sign_att(crypto, csrk, cmd,
+						cmd_len - ATT_SIGNATURE_LEN,
+						r_sign_cnt, t)) {
+			error("gatt: Error when generating att signature");
+			return;
+		}
+
+		if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
+			error("gatt: signature does not match");
+			return;
+		}
+		/* Signature OK, proceed with write */
+		bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK, r_sign_cnt);
+		gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+					&dev->bdaddr, write_confirm, NULL);
+	}
+}
+
 static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
 {
 	struct gatt_device *dev = user_data;
@@ -1475,10 +1701,20 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
 	dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
 							disconnected_cb, dev);
 
-	dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+	dev->req_handler_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+						GATTRIB_ALL_HANDLES,
+						att_req_handler, dev, NULL);
+	dev->write_cmd_handler_id = g_attrib_register(attrib, ATT_OP_WRITE_CMD,
+							GATTRIB_ALL_HANDLES,
+							att_write_cmd_handler,
+							dev, NULL);
+	dev->sign_write_cmd_handler_id = g_attrib_register(attrib,
+						ATT_OP_SIGNED_WRITE_CMD,
 						GATTRIB_ALL_HANDLES,
-						att_handler, dev, NULL);
-	if (dev->server_id == 0)
+						att_sign_write_cmd_handler,
+						dev, NULL);
+	if ((dev->req_handler_id && dev->write_cmd_handler_id &&
+					dev->sign_write_cmd_handler_id) == 0)
 		error("gatt: Could not attach to server");
 
 	device_set_state(dev, DEVICE_CONNECTED);
@@ -2989,37 +3225,6 @@ static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
 	free(data);
 }
 
-static int get_cid(struct gatt_device *dev)
-{
-	GIOChannel *io;
-	uint16_t cid;
-
-	io = g_attrib_get_channel(dev->attrib);
-
-	if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) {
-		error("gatt: Failed to get CID");
-		return -1;
-	}
-
-	return cid;
-}
-
-static int get_sec_level(struct gatt_device *dev)
-{
-	GIOChannel *io;
-	int sec_level;
-
-	io = g_attrib_get_channel(dev->attrib);
-
-	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
-							BT_IO_OPT_INVALID)) {
-		error("gatt: Failed to get sec_level");
-		return -1;
-	}
-
-	return sec_level;
-}
-
 static bool set_security(struct gatt_device *device, int req_sec_level)
 {
 	int sec_level;
@@ -4612,76 +4817,6 @@ struct request_processing_data {
 	struct gatt_device *device;
 };
 
-static uint8_t check_device_permissions(struct gatt_device *device,
-					uint8_t opcode, uint32_t permissions)
-{
-	GIOChannel *io;
-	int sec_level;
-
-	io = g_attrib_get_channel(device->attrib);
-
-	if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
-							BT_IO_OPT_INVALID))
-		return ATT_ECODE_UNLIKELY;
-
-	DBG("opcode %u permissions %u sec_level %u", opcode, permissions,
-								sec_level);
-
-	switch (opcode) {
-	case ATT_OP_SIGNED_WRITE_CMD:
-		if (!(permissions & GATT_PERM_WRITE_SIGNED))
-				return ATT_ECODE_WRITE_NOT_PERM;
-
-		if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) &&
-						sec_level < BT_SECURITY_HIGH)
-			return ATT_ECODE_AUTHENTICATION;
-		break;
-	case ATT_OP_READ_BY_TYPE_REQ:
-	case ATT_OP_READ_REQ:
-	case ATT_OP_READ_BLOB_REQ:
-	case ATT_OP_READ_MULTI_REQ:
-	case ATT_OP_READ_BY_GROUP_REQ:
-	case ATT_OP_FIND_BY_TYPE_REQ:
-	case ATT_OP_FIND_INFO_REQ:
-		if (!(permissions & GATT_PERM_READ))
-			return ATT_ECODE_READ_NOT_PERM;
-
-		if ((permissions & GATT_PERM_READ_MITM) &&
-						sec_level < BT_SECURITY_HIGH)
-			return ATT_ECODE_AUTHENTICATION;
-
-		if ((permissions & GATT_PERM_READ_ENCRYPTED) &&
-						sec_level < BT_SECURITY_MEDIUM)
-			return ATT_ECODE_INSUFF_ENC;
-
-		if (permissions & GATT_PERM_READ_AUTHORIZATION)
-			return ATT_ECODE_AUTHORIZATION;
-		break;
-	case ATT_OP_WRITE_REQ:
-	case ATT_OP_WRITE_CMD:
-	case ATT_OP_PREP_WRITE_REQ:
-	case ATT_OP_EXEC_WRITE_REQ:
-		if (!(permissions & GATT_PERM_WRITE))
-			return ATT_ECODE_WRITE_NOT_PERM;
-
-		if ((permissions & GATT_PERM_WRITE_MITM) &&
-						sec_level < BT_SECURITY_HIGH)
-			return ATT_ECODE_AUTHENTICATION;
-
-		if ((permissions & GATT_PERM_WRITE_ENCRYPTED) &&
-						sec_level < BT_SECURITY_MEDIUM)
-			return ATT_ECODE_INSUFF_ENC;
-
-		if (permissions & GATT_PERM_WRITE_AUTHORIZATION)
-			return ATT_ECODE_AUTHORIZATION;
-		break;
-	default:
-		return ATT_ECODE_UNLIKELY;
-	}
-
-	return 0;
-}
-
 static uint8_t err_to_att(int err)
 {
 	if (!err || (err > 0 && err < UINT8_MAX))
@@ -6180,119 +6315,6 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
 	return 0;
 }
 
-static void write_confirm(struct gatt_db_attribute *attrib,
-						int err, void *user_data)
-{
-	if (!err)
-		return;
-
-	error("Error writting attribute %p", attrib);
-}
-
-static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
-						struct gatt_device *dev)
-{
-	uint8_t value[cmd_len];
-	struct gatt_db_attribute *attrib;
-	uint32_t permissions;
-	uint16_t handle;
-	uint16_t len;
-	size_t vlen;
-
-	len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
-	if (!len)
-		return;
-
-	if (handle == 0)
-		return;
-
-	attrib = gatt_db_get_attribute(gatt_db, handle);
-	if (!attrib)
-		return;
-
-	if (!gatt_db_attribute_get_permissions(attrib, &permissions))
-		return;
-
-	if (check_device_permissions(dev, cmd[0], permissions))
-		return;
-
-	gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr,
-							write_confirm, NULL);
-}
-
-static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
-						struct gatt_device *dev)
-{
-	uint8_t value[cmd_len];
-	uint8_t s[ATT_SIGNATURE_LEN];
-	struct gatt_db_attribute *attrib;
-	uint32_t permissions;
-	uint16_t handle;
-	uint16_t len;
-	size_t vlen;
-	uint8_t csrk[16];
-	uint32_t sign_cnt;
-
-	if (get_cid(dev) != ATT_CID) {
-		error("gatt: Remote tries write signed on BR/EDR bearer");
-		connection_cleanup(dev);
-		return;
-	}
-
-	if (get_sec_level(dev) != BT_SECURITY_LOW) {
-		error("gatt: Remote tries write signed on encrypted link");
-		connection_cleanup(dev);
-		return;
-	}
-
-	if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) {
-		error("gatt: No valid csrk from remote device");
-		return;
-	}
-
-	len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s);
-
-	if (handle == 0)
-		return;
-
-	attrib = gatt_db_get_attribute(gatt_db, handle);
-	if (!attrib)
-		return;
-
-	gatt_db_attribute_get_permissions(attrib, &permissions);
-
-	if (check_device_permissions(dev, cmd[0], permissions))
-		return;
-
-	if (len) {
-		uint8_t t[ATT_SIGNATURE_LEN];
-		uint32_t r_sign_cnt = get_le32(s);
-
-		if (r_sign_cnt < sign_cnt) {
-			error("gatt: Invalid sign counter (%d<%d)",
-							r_sign_cnt, sign_cnt);
-			return;
-		}
-
-		/* Generate signature and verify it */
-		if (!bt_crypto_sign_att(crypto, csrk, cmd,
-						cmd_len - ATT_SIGNATURE_LEN,
-						r_sign_cnt, t)) {
-			error("gatt: Error when generating att signature");
-			return;
-		}
-
-		if (memcmp(t, s, ATT_SIGNATURE_LEN)) {
-			error("gatt: signature does not match");
-			return;
-		}
-		/* Signature OK, proceed with write */
-		bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK, r_sign_cnt);
-		gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
-					&dev->bdaddr, write_confirm, NULL);
-	}
-}
-
 static void attribute_write_cb(struct gatt_db_attribute *attrib, int err,
 								void *user_data)
 {
@@ -6481,7 +6503,8 @@ static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
 	return 0;
 }
 
-static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+static void att_req_handler(const uint8_t *ipdu, uint16_t len,
+							gpointer user_data)
 {
 	struct gatt_device *dev = user_data;
 	uint8_t status;
@@ -6518,14 +6541,6 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
 		if (!status)
 			return;
 		break;
-	case ATT_OP_WRITE_CMD:
-		write_cmd_request(ipdu, len, dev);
-		/* No response on write cmd */
-		return;
-	case ATT_OP_SIGNED_WRITE_CMD:
-		write_signed_cmd_request(ipdu, len, dev);
-		/* No response on write signed cmd */
-		return;
 	case ATT_OP_PREP_WRITE_REQ:
 		status = write_prep_request(ipdu, len, dev);
 		if (!status)
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux