2018-05-25 09:15:38

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ v4 1/4] doc/gatt-api: Add authorization options for attributes

This patch adds authorization property for attributes and prepare write
request for authorization option for write request. This is require to
handle correctly prepare writes, which may response with insufficient
authorization error.
---
doc/gatt-api.txt | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 0f1cc9029..6b1487254 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -85,6 +85,9 @@ Methods array{byte} ReadValue(dict options)
Possible options: "offset": Start offset
"device": Device path (Server only)
"link": Link type (Server only)
+ "prep-authorize": boolean Is prepare
+ authorization
+ request

Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
@@ -250,6 +253,7 @@ Properties string UUID [read-only]
"encrypt-authenticated-write"
"secure-read" (Server only)
"secure-write" (Server only)
+ "authorize"

Characteristic Descriptors hierarchy
====================================
@@ -284,6 +288,9 @@ Methods array{byte} ReadValue(dict flags)
Possible options: "offset": Start offset
"device": Device path (Server only)
"link": Link type (Server only)
+ "prep-authorize": boolean Is prepare
+ authorization
+ request

Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
@@ -321,6 +328,7 @@ Properties string UUID [read-only]
"encrypt-authenticated-write"
"secure-read" (Server Only)
"secure-write" (Server Only)
+ "authorize"

GATT Profile hierarchy
=====================
--
2.13.6



2018-05-28 08:01:07

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 1/4] doc/gatt-api: Add authorization options for attributes

Hi Marcel,
sob., 26 maj 2018 o 18:13 Marcel Holtmann <[email protected]> napisa=C5=
=82(a):

> Hi Grzegorz,

> > This patch adds authorization property for attributes and prepare write
> > request for authorization option for write request. This is require to
> > handle correctly prepare writes, which may response with insufficient
> > authorization error.
> > ---
> > doc/gatt-api.txt | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
> > index 0f1cc9029..6b1487254 100644
> > --- a/doc/gatt-api.txt
> > +++ b/doc/gatt-api.txt
> > @@ -85,6 +85,9 @@ Methods array{byte} ReadValue(dict
options)
> > Possible options: "offset": Start offset
> > "device": Device path (Server
only)
> > "link": Link type (Server only)
> > + "prep-authorize": boolean Is
prepare
> > + authorization
> > + request
> >
> > Possible Errors: org.bluez.Error.Failed
> > org.bluez.Error.InProgress
> > @@ -250,6 +253,7 @@ Properties string UUID [read-only]
> > "encrypt-authenticated-write"
> > "secure-read" (Server only)
> > "secure-write" (Server only)
> > + "authorize"
> >
> > Characteristic Descriptors hierarchy
> > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> > @@ -284,6 +288,9 @@ Methods array{byte} ReadValue(dict flags)
> > Possible options: "offset": Start offset
> > "device": Device path (Server
only)
> > "link": Link type (Server only)
> > + "prep-authorize": boolean Is
prepare
> > + authorization
> > + request

> lets use =E2=80=9Cprepare-authorize=E2=80=9D and not shortcut things for =
no good reason.

Ok.
> Regards

> Marcel


Regards,
Grzegorz

2018-05-26 16:13:55

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 1/4] doc/gatt-api: Add authorization options for attributes

Hi Grzegorz,

> This patch adds authorization property for attributes and prepare write
> request for authorization option for write request. This is require to
> handle correctly prepare writes, which may response with insufficient
> authorization error.
> ---
> doc/gatt-api.txt | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
> index 0f1cc9029..6b1487254 100644
> --- a/doc/gatt-api.txt
> +++ b/doc/gatt-api.txt
> @@ -85,6 +85,9 @@ Methods array{byte} ReadValue(dict options)
> Possible options: "offset": Start offset
> "device": Device path (Server only)
> "link": Link type (Server only)
> + "prep-authorize": boolean Is prepare
> + authorization
> + request
>
> Possible Errors: org.bluez.Error.Failed
> org.bluez.Error.InProgress
> @@ -250,6 +253,7 @@ Properties string UUID [read-only]
> "encrypt-authenticated-write"
> "secure-read" (Server only)
> "secure-write" (Server only)
> + "authorize"
>
> Characteristic Descriptors hierarchy
> ====================================
> @@ -284,6 +288,9 @@ Methods array{byte} ReadValue(dict flags)
> Possible options: "offset": Start offset
> "device": Device path (Server only)
> "link": Link type (Server only)
> + "prep-authorize": boolean Is prepare
> + authorization
> + request

lets use “prepare-authorize” and not shortcut things for no good reason.

Regards

Marcel


2018-05-25 09:20:23

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ v4 2/4] 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.
---
src/gatt-database.c | 108 ++++++++++++++++++++++++++++++++++++++---------
src/shared/gatt-server.c | 64 ++++++++++++++++++++++++----
2 files changed, 142 insertions(+), 30 deletions(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 0ac5b75b0..8a03bcc25 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 {
@@ -1371,7 +1377,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;

@@ -1430,6 +1437,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;
@@ -1442,7 +1451,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;

@@ -1470,6 +1480,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;
@@ -1480,7 +1492,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;
@@ -1495,9 +1507,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,
@@ -1545,7 +1558,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;
}
@@ -1627,7 +1641,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;
}
@@ -1937,6 +1952,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, "prep-authorize", DBUS_TYPE_BOOLEAN,
+ &op->prep_authorize);
}

static void read_setup_cb(DBusMessageIter *iter, void *user_data)
@@ -2008,6 +2026,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;
@@ -2027,6 +2047,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)) {
/*
@@ -2045,9 +2075,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;

@@ -2062,6 +2093,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;
@@ -2073,12 +2106,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,
@@ -2191,7 +2227,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)
@@ -2229,7 +2265,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,
@@ -2532,8 +2568,24 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
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:
@@ -2614,6 +2666,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 (pipe_io_send(chrc->write_io, value, len) < 0) {
error("Unable to write: %s", strerror(errno));
@@ -2630,13 +2701,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 4b554f665..cdade76f8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1208,6 +1208,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)
{
@@ -1215,7 +1254,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;
@@ -1245,15 +1285,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.13.6


2018-05-25 09:15:40

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ v4 3/4] client: Add authorized property handling to characteristic attribute

This patch adds handling of characteristic prepare write authorized
property to bluetoothctl.
---
client/gatt.c | 146 +++++++++++++++++++++++++++++++++++++---------------------
client/main.c | 9 +---
2 files changed, 96 insertions(+), 59 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index d37a6f55c..3fde3ca2e 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -80,7 +80,6 @@ struct chrc {
struct io *write_io;
struct io *notify_io;
bool authorization_req;
- bool authorized;
};

struct service {
@@ -1609,7 +1608,8 @@ static const char *path_to_address(const char *path)
}

static int parse_options(DBusMessageIter *iter, uint16_t *offset, uint16_t *mtu,
- char **device, char **link)
+ char **device, char **link,
+ bool *prep_authorize)
{
DBusMessageIter dict;

@@ -1650,6 +1650,12 @@ static int parse_options(DBusMessageIter *iter, uint16_t *offset, uint16_t *mtu,
return -EINVAL;
if (link)
dbus_message_iter_get_basic(&value, link);
+ } else if (strcasecmp(key, "prep-authorize") == 0) {
+ if (var != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ if (prep_authorize)
+ dbus_message_iter_get_basic(&value,
+ prep_authorize);
}

dbus_message_iter_next(&dict);
@@ -1703,8 +1709,6 @@ static void authorize_read_response(const char *input, void *user_data)
reply = read_value(pending_message, &chrc->value[aad->offset],
chrc->value_len - aad->offset);

- chrc->authorized = true;
-
g_dbus_send_message(aad->conn, reply);

g_free(aad);
@@ -1727,18 +1731,15 @@ static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,

dbus_message_iter_init(msg, &iter);

- if (parse_options(&iter, &offset, NULL, &device, &link))
+ if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidArguments",
NULL);

bt_shell_printf("ReadValue: %s offset %u link %s\n",
- path_to_address(device), offset, link);
-
- if (chrc->authorization_req && offset == 0)
- chrc->authorized = false;
+ path_to_address(device), offset, link);

- if (chrc->authorization_req && !chrc->authorized) {
+ if (chrc->authorization_req) {
struct authorize_attribute_data *aad;

aad = g_new0(struct authorize_attribute_data, 1);
@@ -1765,33 +1766,31 @@ static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
return read_value(msg, &chrc->value[offset], chrc->value_len - offset);
}

-static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len,
- int max_len)
+static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
{
DBusMessageIter array;
- uint16_t offset = 0;
- uint8_t *read_value;
- int read_len;

if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return -EINVAL;

dbus_message_iter_recurse(iter, &array);
- dbus_message_iter_get_fixed_array(&array, &read_value, &read_len);
+ dbus_message_iter_get_fixed_array(&array, value, len);

- dbus_message_iter_next(iter);
- if (parse_options(iter, &offset, NULL, NULL, NULL))
- return -EINVAL;
+ return 0;
+}

- if ((offset + read_len) > max_len)
+static int write_value(int *dst_len, uint8_t **dst_value, uint8_t *src_val,
+ int src_len, uint16_t offset, uint16_t max_len)
+{
+ if ((offset + src_len) > max_len)
return -EOVERFLOW;

- if ((offset + read_len) > *len) {
- *len = offset + read_len;
- *value = g_realloc(*value, *len);
+ if ((offset + src_len) != *dst_len) {
+ *dst_len = offset + src_len;
+ *dst_value = g_realloc(*dst_value, *dst_len);
}

- memcpy(*value + offset, read_value, read_len);
+ memcpy(*dst_value + offset, src_val, src_len);

return 0;
}
@@ -1800,12 +1799,26 @@ static void authorize_write_response(const char *input, void *user_data)
{
struct authorize_attribute_data *aad = user_data;
struct chrc *chrc = aad->attribute;
+ bool prep_authorize = false;
DBusMessageIter iter;
DBusMessage *reply;
+ int value_len;
+ uint8_t *value;
char *err;
- int errsv;

dbus_message_iter_init(pending_message, &iter);
+ if (parse_value_arg(&iter, &value, &value_len)) {
+ err = "org.bluez.Error.InvalidArguments";
+
+ goto error;
+ }
+
+ dbus_message_iter_next(&iter);
+ if (parse_options(&iter, NULL, NULL, NULL, NULL, &prep_authorize)) {
+ err = "org.bluez.Error.InvalidArguments";
+
+ goto error;
+ }

if (!strcmp(input, "no")) {
err = "org.bluez.Error.NotAuthorized";
@@ -1813,15 +1826,17 @@ static void authorize_write_response(const char *input, void *user_data)
goto error;
}

- chrc->authorized = true;
+ /* Authorization check of prepare writes */
+ if (prep_authorize) {
+ reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID);
+ g_dbus_send_message(aad->conn, reply);
+ g_free(aad);

- errsv = parse_value_arg(&iter, &chrc->value, &chrc->value_len,
- chrc->max_val_len);
- if (errsv == -EINVAL) {
- err = "org.bluez.Error.InvalidArguments";
+ return;
+ }

- goto error;
- } else if (errsv == -EOVERFLOW) {
+ if (write_value(&chrc->value_len, &chrc->value, value, value_len,
+ aad->offset, chrc->max_val_len)) {
err = "org.bluez.Error.InvalidValueLength";

goto error;
@@ -1848,18 +1863,31 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
struct chrc *chrc = user_data;
+ uint16_t offset = 0;
+ bool prep_authorize = false;
DBusMessageIter iter;
+ int value_len;
+ uint8_t *value;
char *str;
- int errsv;

dbus_message_iter_init(msg, &iter);

- if (chrc->authorization_req && !chrc->authorized) {
+ if (parse_value_arg(&iter, &value, &value_len))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments", NULL);
+
+ dbus_message_iter_next(&iter);
+ if (parse_options(&iter, &offset, NULL, NULL, NULL, &prep_authorize))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments", NULL);
+
+ if (chrc->authorization_req) {
struct authorize_attribute_data *aad;

aad = g_new0(struct authorize_attribute_data, 1);
aad->conn = conn;
aad->attribute = chrc;
+ aad->offset = offset;

str = g_strdup_printf("Authorize attribute(%s) write (yes/no):",
chrc->path);
@@ -1873,15 +1901,14 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
return NULL;
}

- errsv = parse_value_arg(&iter, &chrc->value, &chrc->value_len,
- chrc->max_val_len);
- if (errsv == -EINVAL) {
- return g_dbus_create_error(msg,
- "org.bluez.Error.InvalidArguments", NULL);
- } else if (errsv == -EOVERFLOW) {
+ /* Authorization check of prepare writes */
+ if (prep_authorize)
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+ if (write_value(&chrc->value_len, &chrc->value, value, value_len,
+ offset, chrc->max_val_len))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidValueLength", NULL);
- }

bt_shell_printf("[" COLORED_CHG "] Attribute %s written" , chrc->path);

@@ -1943,7 +1970,7 @@ static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
"org.bluez.Error.NotPermitted",
NULL);

- if (parse_options(&iter, NULL, &chrc->mtu, &device, &link))
+ if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidArguments",
NULL);
@@ -1975,7 +2002,7 @@ static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
"org.bluez.Error.NotPermitted",
NULL);

- if (parse_options(&iter, NULL, &chrc->mtu, &device, &link))
+ if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidArguments",
NULL);
@@ -2101,6 +2128,18 @@ static void chrc_set_value(const char *input, void *user_data)
chrc->max_val_len = chrc->value_len;
}

+static gboolean attr_authorization_flag_exists(char **flags)
+{
+ int i;
+
+ for (i = 0; flags[i]; i++) {
+ if (!strcmp("authorize", flags[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
int argc, char *argv[])
{
@@ -2119,7 +2158,7 @@ void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
chrc->uuid = g_strdup(argv[1]);
chrc->path = g_strdup_printf("%s/chrc%p", service->path, chrc);
chrc->flags = g_strsplit(argv[2], ",", -1);
- chrc->authorization_req = argc > 3 ? true : false;
+ chrc->authorization_req = attr_authorization_flag_exists(chrc->flags);

if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
chrc_methods, NULL, chrc_properties,
@@ -2191,7 +2230,7 @@ static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,

dbus_message_iter_init(msg, &iter);

- if (parse_options(&iter, &offset, NULL, &device, &link))
+ if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidArguments",
NULL);
@@ -2213,21 +2252,24 @@ static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
DBusMessageIter iter;
uint16_t offset = 0;
char *device = NULL, *link = NULL;
+ int value_len;
+ uint8_t *value;

dbus_message_iter_init(msg, &iter);

- if (parse_value_arg(&iter, &desc->value, &desc->value_len,
- desc->max_val_len))
+ if (parse_value_arg(&iter, &value, &value_len))
return g_dbus_create_error(msg,
- "org.bluez.Error.InvalidArguments",
- NULL);
+ "org.bluez.Error.InvalidArguments", NULL);

dbus_message_iter_next(&iter);
+ if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
+ return g_dbus_create_error(msg,
+ "org.bluez.Error.InvalidArguments", NULL);

- if (parse_options(&iter, &offset, NULL, &device, &link))
+ if (write_value(&desc->value_len, &desc->value, value,
+ value_len, offset, desc->max_val_len))
return g_dbus_create_error(msg,
- "org.bluez.Error.InvalidArguments",
- NULL);
+ "org.bluez.Error.InvalidValueLength", NULL);

bt_shell_printf("WriteValue: %s offset %u link %s\n",
path_to_address(device), offset, link);
diff --git a/client/main.c b/client/main.c
index 180f841ae..87323d8f7 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2036,11 +2036,6 @@ static void cmd_register_characteristic(int argc, char *argv[])
if (check_default_ctrl() == FALSE)
return bt_shell_noninteractive_quit(EXIT_FAILURE);

- if (argc > 3 && strcmp(argv[3], "authorize")) {
- bt_shell_printf("Invalid authorize argument\n");
- return bt_shell_noninteractive_quit(EXIT_FAILURE);
- }
-
gatt_register_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
}

@@ -2517,8 +2512,8 @@ static const struct bt_shell_menu gatt_menu = {
cmd_unregister_includes,
"Unregister Included service." },
{ "register-characteristic", "<UUID> <Flags=read,write,notify...> "
- "[authorize]", cmd_register_characteristic,
- "Register application characteristic" },
+ , cmd_register_characteristic,
+ "Register application characteristic" },
{ "unregister-characteristic", "<UUID/object>",
cmd_unregister_characteristic,
"Unregister application characteristic" },
--
2.13.6


2018-05-25 09:15:41

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ v4 4/4] client: Don't require authorization for trusted devices

This patch adds possibility to ommit authorization request from trusted
devices.
---
client/gatt.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/client/gatt.c b/client/gatt.c
index 3fde3ca2e..8128f43fc 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -1720,6 +1720,20 @@ error:
g_free(aad);
}

+static bool is_device_trusted(const char *path)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ bool trusted;
+
+ proxy = bt_shell_get_env(path);
+
+ if (g_dbus_proxy_get_property(proxy, "Trusted", &iter))
+ dbus_message_iter_get_basic(&iter, &trusted);
+
+ return trusted;
+}
+
static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
@@ -1739,7 +1753,7 @@ static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
bt_shell_printf("ReadValue: %s offset %u link %s\n",
path_to_address(device), offset, link);

- if (chrc->authorization_req) {
+ if (!is_device_trusted(device) && chrc->authorization_req) {
struct authorize_attribute_data *aad;

aad = g_new0(struct authorize_attribute_data, 1);
@@ -1865,6 +1879,7 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
struct chrc *chrc = user_data;
uint16_t offset = 0;
bool prep_authorize = false;
+ char *device = NULL;
DBusMessageIter iter;
int value_len;
uint8_t *value;
@@ -1877,11 +1892,11 @@ static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
"org.bluez.Error.InvalidArguments", NULL);

dbus_message_iter_next(&iter);
- if (parse_options(&iter, &offset, NULL, NULL, NULL, &prep_authorize))
+ if (parse_options(&iter, &offset, NULL, &device, NULL, &prep_authorize))
return g_dbus_create_error(msg,
"org.bluez.Error.InvalidArguments", NULL);

- if (chrc->authorization_req) {
+ if (!is_device_trusted(device) && chrc->authorization_req) {
struct authorize_attribute_data *aad;

aad = g_new0(struct authorize_attribute_data, 1);
--
2.13.6


2018-05-25 09:15:39

by Grzegorz Kołodziejczyk

[permalink] [raw]
Subject: [PATCH BlueZ v4 2/4] 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.
---
src/gatt-database.c | 108 ++++++++++++++++++++++++++++++++++++++---------
src/shared/gatt-server.c | 64 ++++++++++++++++++++++++----
2 files changed, 142 insertions(+), 30 deletions(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 0ac5b75b0..fe9abf1e5 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 {
@@ -1371,7 +1377,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;

@@ -1430,6 +1437,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;
@@ -1442,7 +1451,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;

@@ -1470,6 +1480,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;
@@ -1480,7 +1492,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;
@@ -1495,9 +1507,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,
@@ -1545,7 +1558,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;
}
@@ -1627,7 +1641,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;
}
@@ -1937,6 +1952,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, "prep-authorize", DBUS_TYPE_BOOLEAN,
+ &op->prep_authorize);
}

static void read_setup_cb(DBusMessageIter *iter, void *user_data)
@@ -2008,6 +2026,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;
@@ -2027,6 +2047,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)) {
/*
@@ -2045,9 +2075,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;

@@ -2062,6 +2093,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;
@@ -2073,12 +2106,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,
@@ -2191,7 +2227,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)
@@ -2229,7 +2265,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,
@@ -2532,8 +2568,24 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
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:
@@ -2614,6 +2666,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 (id == BT_ATT_OP_EXEC_WRITE_REQ)
+ chrc->prep_authorized = false;
+
if (chrc->write_io) {
if (pipe_io_send(chrc->write_io, value, len) < 0) {
error("Unable to write: %s", strerror(errno));
@@ -2630,13 +2701,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 4b554f665..cdade76f8 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -1208,6 +1208,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)
{
@@ -1215,7 +1254,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;
@@ -1245,15 +1285,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.13.6