From 6a8095c879956ba55e3796cf93bd5225f51f652b Mon Sep 17 00:00:00 2001 From: Grzegorz Kolodziejczyk Date: Mon, 28 May 2018 10:20:52 +0200 Subject: [PATCH] shared/gatt-server: Request authorization for prepare writes This patch adds gatt-server possibility to request authorization from application if needed and previously wasn't authorized. Authorization is requested by sending message with set prepare write authorization reqest to client. Change-Id: Ic1d8eca62c8df593d8f7d035451c63eb7267b889 Signed-off-by: Amit Purwar --- src/gatt-database.c | 108 ++++++++++++++++++++++++++++++++++++++--------- src/shared/gatt-server.c | 64 ++++++++++++++++++++++++---- 2 files changed, 143 insertions(+), 29 deletions(-) diff --git a/src/gatt-database.c b/src/gatt-database.c index 54d948c..f64c15e 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -133,6 +133,8 @@ struct external_chrc { struct queue *pending_reads; struct queue *pending_writes; unsigned int ntfy_cnt; + bool prep_authorized; + bool req_prep_authorization; }; struct external_desc { @@ -144,6 +146,8 @@ struct external_desc { bool handled; struct queue *pending_reads; struct queue *pending_writes; + bool prep_authorized; + bool req_prep_authorization; }; struct pending_op { @@ -154,6 +158,8 @@ struct pending_op { struct gatt_db_attribute *attrib; struct queue *owner_queue; struct iovec data; + bool is_characteristic; + bool prep_authorize; }; struct notify { @@ -1724,7 +1730,8 @@ static bool incr_attr_count(struct external_service *service, uint16_t incr) } static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props, - uint8_t *ext_props, uint32_t *perm) + uint8_t *ext_props, uint32_t *perm, + bool *req_prep_authorization) { const char *flag; @@ -1783,6 +1790,8 @@ static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props, *props |= BT_GATT_CHRC_PROP_WRITE; *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE; *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; + } else if (!strcmp("authorize", flag)) { + *req_prep_authorization = true; } else { error("Invalid characteristic flag: %s", flag); return false; @@ -1795,7 +1804,8 @@ static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props, return true; } -static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm) +static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm, + bool *req_prep_authorization) { const char *flag; @@ -1823,6 +1833,8 @@ static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm) *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE; else if (!strcmp("secure-write", flag)) *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; + else if (!strcmp("authorize", flag)) + *req_prep_authorization = true; else { error("Invalid descriptor flag: %s", flag); return false; @@ -1833,7 +1845,7 @@ static bool parse_desc_flags(DBusMessageIter *array, uint32_t *perm) } static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props, - uint32_t *perm) + uint32_t *perm, bool *req_prep_authorization) { DBusMessageIter iter, array; const char *iface; @@ -1848,9 +1860,10 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props, iface = g_dbus_proxy_get_interface(proxy); if (!strcmp(iface, GATT_DESC_IFACE)) - return parse_desc_flags(&array, perm); + return parse_desc_flags(&array, perm, req_prep_authorization); - return parse_chrc_flags(&array, props, ext_props, perm); + return parse_chrc_flags(&array, props, ext_props, perm, + req_prep_authorization); } static struct external_chrc *chrc_create(struct gatt_app *app, @@ -1898,7 +1911,8 @@ static struct external_chrc *chrc_create(struct gatt_app *app, * are used to determine if any special descriptors should be * created. */ - if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) { + if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm, + &chrc->req_prep_authorization)) { error("Failed to parse characteristic properties"); goto fail; } @@ -1980,7 +1994,8 @@ static struct external_desc *desc_create(struct gatt_app *app, * Parse descriptors flags here since they are used to * determine the permission the descriptor should have */ - if (!parse_flags(proxy, NULL, NULL, &desc->perm)) { + if (!parse_flags(proxy, NULL, NULL, &desc->perm, + &desc->req_prep_authorization)) { error("Failed to parse characteristic properties"); goto fail; } @@ -2313,6 +2328,9 @@ static void append_options(DBusMessageIter *iter, void *user_data) &op->offset); if (link) dict_append_entry(iter, "link", DBUS_TYPE_STRING, &link); + if (op->prep_authorize) + dict_append_entry(iter, "prepare-authorize", DBUS_TYPE_BOOLEAN, + &op->prep_authorize); } static void read_setup_cb(DBusMessageIter *iter, void *user_data) @@ -2421,6 +2439,8 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data) static void write_reply_cb(DBusMessage *message, void *user_data) { struct pending_op *op = user_data; + struct external_chrc *chrc; + struct external_desc *desc; DBusError err; DBusMessageIter iter; uint8_t ecode = 0; @@ -2444,6 +2464,16 @@ static void write_reply_cb(DBusMessage *message, void *user_data) goto done; } + if (op->prep_authorize) { + if (op->is_characteristic) { + chrc = gatt_db_attribute_get_user_data(op->attrib); + chrc->prep_authorized = true; + } else { + desc = gatt_db_attribute_get_user_data(op->attrib); + desc->prep_authorized = true; + } + } + dbus_message_iter_init(message, &iter); if (dbus_message_iter_has_next(&iter)) { /* @@ -2462,9 +2492,10 @@ static struct pending_op *pending_write_new(struct btd_device *device, struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id, - const uint8_t *value, - size_t len, - uint16_t offset, uint8_t link_type) + const uint8_t *value, size_t len, + uint16_t offset, uint8_t link_type, + bool is_characteristic, + bool prep_authorize) { struct pending_op *op; @@ -2479,6 +2510,8 @@ static struct pending_op *pending_write_new(struct btd_device *device, op->id = id; op->offset = offset; op->link_type = link_type; + op->is_characteristic = is_characteristic; + op->prep_authorize = prep_authorize; queue_push_tail(owner_queue, op); return op; @@ -2490,12 +2523,15 @@ static struct pending_op *send_write(struct btd_device *device, struct queue *owner_queue, unsigned int id, const uint8_t *value, size_t len, - uint16_t offset, uint8_t link_type) + uint16_t offset, uint8_t link_type, + bool is_characteristic, + bool prep_authorize) { struct pending_op *op; op = pending_write_new(device, owner_queue, attrib, id, value, len, - offset, link_type); + offset, link_type, is_characteristic, + prep_authorize); if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb, owner_queue ? write_reply_cb : NULL, @@ -2617,7 +2653,7 @@ static void acquire_write_reply(DBusMessage *message, void *user_data) retry: send_write(op->device, op->attrib, chrc->proxy, NULL, op->id, op->data.iov_base, op->data.iov_len, 0, - op->link_type); + op->link_type, false, false); } static void acquire_write_setup(DBusMessageIter *iter, void *user_data) @@ -2655,7 +2691,7 @@ static struct pending_op *acquire_write(struct external_chrc *chrc, struct pending_op *op; op = pending_write_new(device, NULL, attrib, id, value, len, 0, - link_type); + link_type, false, false); if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite", acquire_write_setup, @@ -3041,8 +3077,25 @@ static void desc_write_cb(struct gatt_db_attribute *attrib, error("Unable to find device object"); goto fail; } + + if (opcode == BT_ATT_OP_PREP_WRITE_REQ) { + if (!desc->prep_authorized && desc->req_prep_authorization) + send_write(device, attrib, desc->proxy, + desc->pending_writes, id, value, len, + offset, bt_att_get_link_type(att), + false, true); + else + gatt_db_attribute_write_result(attrib, id, 0); + + return; + } + + if (opcode == BT_ATT_OP_EXEC_WRITE_REQ) + desc->prep_authorized = false; + if (send_write(device, attrib, desc->proxy, desc->pending_writes, id, - value, len, offset, bt_att_get_link_type(att))) + value, len, offset, bt_att_get_link_type(att), false, + false)) return; fail: @@ -3122,6 +3175,25 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib, goto fail; } + if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) + queue = chrc->pending_writes; + else + queue = NULL; + + if (opcode == BT_ATT_OP_PREP_WRITE_REQ) { + if (!chrc->prep_authorized && chrc->req_prep_authorization) + send_write(device, attrib, chrc->proxy, queue, + id, value, len, offset, + bt_att_get_link_type(att), true, true); + else + gatt_db_attribute_write_result(attrib, id, 0); + + return; + } + + if (opcode == BT_ATT_OP_EXEC_WRITE_REQ) + chrc->prep_authorized = false; + if (chrc->write_io) { if (sock_io_send(chrc->write_io, value, len) < 0) { error("Unable to write: %s", strerror(errno)); @@ -3138,12 +3210,8 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib, return; } - if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) - queue = chrc->pending_writes; - else - queue = NULL; if (send_write(device, attrib, chrc->proxy, queue, id, value, len, - offset, bt_att_get_link_type(att))) + offset, bt_att_get_link_type(att), false, false)) return; fail: diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index b0e9679..858c3a7 100755 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -1247,6 +1247,45 @@ static bool store_prep_data(struct bt_gatt_server *server, return prep_data_new(server, handle, offset, length, value); } +struct prep_write_complete_data { + void *pdu; + uint16_t length; + struct bt_gatt_server *server; +}; + +static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, + void *user_data) +{ + struct prep_write_complete_data *pwcd = user_data; + uint16_t handle = 0; + uint16_t offset; + + handle = get_le16(pwcd->pdu); + + if (err) { + bt_att_send_error_rsp(pwcd->server->att, + BT_ATT_OP_PREP_WRITE_REQ, handle, err); + free(pwcd->pdu); + free(pwcd); + + return; + } + + offset = get_le16(pwcd->pdu + 2); + + if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4, + &((uint8_t *) pwcd->pdu)[4])) + bt_att_send_error_rsp(pwcd->server->att, + BT_ATT_OP_PREP_WRITE_RSP, handle, + BT_ATT_ERROR_INSUFFICIENT_RESOURCES); + + bt_att_send(pwcd->server->att, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, + pwcd->length, NULL, NULL, NULL); + + free(pwcd->pdu); + free(pwcd); +} + static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { @@ -1254,7 +1293,8 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; - uint8_t ecode; + struct prep_write_complete_data *pwcd; + uint8_t ecode, status; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; @@ -1284,15 +1324,21 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, if (ecode) goto error; - if (!store_prep_data(server, handle, offset, length - 4, - &((uint8_t *) pdu)[4])) { - ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; - goto error; - } + pwcd = new0(struct prep_write_complete_data, 1); + pwcd->pdu = malloc(length); + memcpy(pwcd->pdu, pdu, length); + pwcd->length = length; + pwcd->server = server; - bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL, - NULL, NULL); - return; + status = gatt_db_attribute_write(attr, offset, NULL, 0, + BT_ATT_OP_PREP_WRITE_REQ, + server->att, + prep_write_complete_cb, pwcd); + + if (status) + return; + + ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, opcode, handle, ecode); -- 2.7.4