From e537e00b1bc1325945dc9d29df13100d6ba2280a Mon Sep 17 00:00:00 2001 From: Kamil Piszczek Date: Fri, 12 Jan 2024 11:30:21 +0100 Subject: [PATCH] [nrf fromtree] bluetooth: gatt: add authorization callback API for gatt operations Added the GATT authorization callback API that allows the user to define application-specific authorization logic for GATT operations. Signed-off-by: Kamil Piszczek (cherry picked from commit 6852abf521ba3a56c2aa9fa21040d9341adb84bd) --- include/zephyr/bluetooth/gatt.h | 51 ++++++++++++++++++ subsys/bluetooth/host/Kconfig.gatt | 9 ++++ subsys/bluetooth/host/att.c | 83 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/include/zephyr/bluetooth/gatt.h b/include/zephyr/bluetooth/gatt.h index c53422648f0..eb38f765afb 100644 --- a/include/zephyr/bluetooth/gatt.h +++ b/include/zephyr/bluetooth/gatt.h @@ -235,6 +235,37 @@ struct bt_gatt_cb { sys_snode_t node; }; +/** @brief GATT authorization callback structure. */ +struct bt_gatt_authorization_cb { + /** @brief Authorize the GATT read operation. + * + * This callback allows the application to authorize the GATT + * read operation for the attribute that is being read. + * + * @param conn Connection object. + * @param attr The attribute that is being read. + * + * @retval true Authorize the operation and allow it to execute. + * @retval false Reject the operation and prevent it from executing. + */ + bool (*read_operation_authorize)(struct bt_conn *conn, + const struct bt_gatt_attr *attr); + + /** @brief Authorize the GATT write operation. + * + * This callback allows the application to authorize the GATT + * write operation for the attribute that is being written. + * + * @param conn Connection object. + * @param attr The attribute that is being written. + * + * @retval true Authorize the operation and allow it to execute. + * @retval false Reject the operation and prevent it from executing. + */ + bool (*write_operation_authorize)(struct bt_conn *conn, + const struct bt_gatt_attr *attr); +}; + /** Characteristic Properties Bit field values */ /** @@ -377,6 +408,26 @@ struct bt_gatt_cpf { */ void bt_gatt_cb_register(struct bt_gatt_cb *cb); +/** @brief Register GATT authorization callbacks. + * + * Register callbacks to perform application-specific authorization of GATT + * operations on all registered GATT attributes. The callback structure must + * remain valid throughout the entire duration of the Bluetooth subsys + * activity. + * + * The @kconfig{CONFIG_BT_GATT_AUTHORIZATION_CUSTOM} Kconfig must be enabled + * to make this API functional. + * + * This API allows the user to register only one callback structure + * concurrently. Passing NULL unregisters the previous set of callbacks + * and makes it possible to register a new one. + * + * @param cb Callback struct. + * + * @return Zero on success or negative error code otherwise + */ +int bt_gatt_authorization_cb_register(const struct bt_gatt_authorization_cb *cb); + /** @brief Register GATT service. * * Register GATT service. Applications can make use of diff --git a/subsys/bluetooth/host/Kconfig.gatt b/subsys/bluetooth/host/Kconfig.gatt index 9209368f943..a2364bbe557 100644 --- a/subsys/bluetooth/host/Kconfig.gatt +++ b/subsys/bluetooth/host/Kconfig.gatt @@ -276,4 +276,13 @@ config DEVICE_NAME_GATT_WRITABLE_AUTHEN endif #BT_DEVICE_NAME_GATT_WRITABLE +config BT_GATT_AUTHORIZATION_CUSTOM + bool "Custom authorization of GATT operations [EXPERIMENTAL]" + select EXPERIMENTAL + help + This option allows the user to define application-specific + authorization logic for GATT operations that can be registered + with the bt_gatt_authorization_cb_register API. See the API + documentation for more details. + endmenu diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index 130b4ab0853..44862f5bf25 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -115,6 +115,11 @@ static uint16_t bt_att_mtu(struct bt_att_chan *chan) return MIN(chan->chan.rx.mtu, chan->chan.tx.mtu); } +/* Descriptor of application-specific authorization callbacks that are used + * with the CONFIG_BT_GATT_AUTHORIZATION_CUSTOM Kconfig enabled. + */ +const static struct bt_gatt_authorization_cb *authorization_cb; + /* ATT connection specific data */ struct bt_att { struct bt_conn *conn; @@ -1289,6 +1294,20 @@ struct read_type_data { typedef bool (*attr_read_cb)(struct net_buf *buf, ssize_t read, void *user_data); +static bool attr_read_authorize(struct bt_conn *conn, + const struct bt_gatt_attr *attr) +{ + if (!IS_ENABLED(CONFIG_BT_GATT_AUTHORIZATION_CUSTOM)) { + return true; + } + + if (!authorization_cb || !authorization_cb->read_operation_authorize) { + return true; + } + + return authorization_cb->read_operation_authorize(conn, attr); +} + static bool attr_read_type_cb(struct net_buf *frag, ssize_t read, void *user_data) { @@ -1398,6 +1417,12 @@ static uint8_t read_type_cb(const struct bt_gatt_attr *attr, uint16_t handle, return BT_GATT_ITER_STOP; } + /* Check the attribute authorization logic */ + if (!attr_read_authorize(conn, attr)) { + data->err = BT_ATT_ERR_AUTHORIZATION; + return BT_GATT_ITER_STOP; + } + /* * If any attribute is founded in handle range it means that error * should be changed from pre-set: attr not found error to no error. @@ -1526,6 +1551,12 @@ static uint8_t read_cb(const struct bt_gatt_attr *attr, uint16_t handle, return BT_GATT_ITER_STOP; } + /* Check the attribute authorization logic */ + if (!attr_read_authorize(conn, attr)) { + data->err = BT_ATT_ERR_AUTHORIZATION; + return BT_GATT_ITER_STOP; + } + /* Read attribute value and store in the buffer */ ret = att_chan_read(chan, attr, data->buf, data->offset, NULL, NULL); if (ret < 0) { @@ -1693,6 +1724,12 @@ static uint8_t read_vl_cb(const struct bt_gatt_attr *attr, uint16_t handle, return BT_GATT_ITER_STOP; } + /* Check the attribute authorization logic */ + if (!attr_read_authorize(conn, attr)) { + data->err = BT_ATT_ERR_AUTHORIZATION; + return BT_GATT_ITER_STOP; + } + /* The Length Value Tuple List may be truncated within the first two * octets of a tuple due to the size limits of the current ATT_MTU. */ @@ -1940,6 +1977,20 @@ struct write_data { uint8_t err; }; +static bool attr_write_authorize(struct bt_conn *conn, + const struct bt_gatt_attr *attr) +{ + if (!IS_ENABLED(CONFIG_BT_GATT_AUTHORIZATION_CUSTOM)) { + return true; + } + + if (!authorization_cb || !authorization_cb->write_operation_authorize) { + return true; + } + + return authorization_cb->write_operation_authorize(conn, attr); +} + static uint8_t write_cb(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data) { @@ -1956,6 +2007,12 @@ static uint8_t write_cb(const struct bt_gatt_attr *attr, uint16_t handle, return BT_GATT_ITER_STOP; } + /* Check the attribute authorization logic */ + if (!attr_write_authorize(data->conn, attr)) { + data->err = BT_ATT_ERR_AUTHORIZATION; + return BT_GATT_ITER_STOP; + } + /* Set command flag if not a request */ if (!data->req) { flags |= BT_GATT_WRITE_FLAG_CMD; @@ -2069,6 +2126,12 @@ static uint8_t prep_write_cb(const struct bt_gatt_attr *attr, uint16_t handle, return BT_GATT_ITER_STOP; } + /* Check the attribute authorization logic */ + if (!attr_write_authorize(data->conn, attr)) { + data->err = BT_ATT_ERR_AUTHORIZATION; + return BT_GATT_ITER_STOP; + } + /* Check if attribute requires handler to accept the data */ if (!(attr->perm & BT_GATT_PERM_PREPARE_WRITE)) { goto append; @@ -3997,3 +4060,23 @@ bool bt_att_chan_opt_valid(struct bt_conn *conn, enum bt_att_chan_opt chan_opt) return true; } + +int bt_gatt_authorization_cb_register(const struct bt_gatt_authorization_cb *cb) +{ + if (!IS_ENABLED(CONFIG_BT_GATT_AUTHORIZATION_CUSTOM)) { + return -ENOSYS; + } + + if (!cb) { + authorization_cb = NULL; + return 0; + } + + if (authorization_cb) { + return -EALREADY; + } + + authorization_cb = cb; + + return 0; +}