gatt: Allow GATT server to dicate CCC permissions
authorDagan Martinez <dmartinez@starry.com>
Fri, 1 Oct 2021 14:17:18 +0000 (10:17 -0400)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 11 Mar 2022 13:38:37 +0000 (19:08 +0530)
Allow a GATT server to impose write permissions/restrictions on a CCC by
setting additional `X-notify` and `X-indicate` flags on its associated
characteristic.

This allows a developer to require encryption/authentication in order
for a GATT client to subscribe to server-initiated updates.

```
[bluetooth]# register-characteristic\
4b75f0f8-1f23-46b1-900c-5bbabcd5ca93 encrypt-read,encrypt-notify

[NEW] Characteristic (Handle 0x0000)
        /org/bluez/app/service0/chrc17
        4b75f0f8-1f23-46b1-900c-5bbabcd5ca93
        Vendor specific
[/org/bluez/app/service0/chrc17] Enter value: 42
```

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
src/gatt-database.c

index fb66bf7..084d33d 100644 (file)
@@ -115,6 +115,7 @@ struct external_chrc {
        uint8_t props;
        uint8_t ext_props;
        uint32_t perm;
+       uint32_t ccc_perm;
        uint16_t mtu;
        struct io *write_io;
        struct io *notify_io;
@@ -1236,7 +1237,7 @@ static struct gatt_db_attribute *
 service_add_ccc(struct gatt_db_attribute *service,
                                struct btd_gatt_database *database,
                                btd_gatt_database_ccc_write_t write_callback,
-                               void *user_data,
+                               void *user_data, uint32_t perm,
                                btd_gatt_database_destroy_t destroy)
 {
        struct gatt_db_attribute *ccc;
@@ -1245,9 +1246,14 @@ service_add_ccc(struct gatt_db_attribute *service,
 
        ccc_cb = new0(struct ccc_cb_data, 1);
 
+       /*
+        * Provide a way for the permissions on a characteristic to dictate
+        * the permissions on the CCC
+        */
+       perm |= BT_ATT_PERM_READ | BT_ATT_PERM_WRITE;
+
        bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-       ccc = gatt_db_service_add_descriptor(service, &uuid,
-                               BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+       ccc = gatt_db_service_add_descriptor(service, &uuid, perm,
                                gatt_ccc_read_cb, gatt_ccc_write_cb, database);
        if (!ccc) {
                error("Failed to create CCC entry in database");
@@ -1448,7 +1454,7 @@ static void populate_gatt_service(struct btd_gatt_database *database)
                                NULL, NULL, database);
 
        database->svc_chngd_ccc = service_add_ccc(service, database, NULL, NULL,
-                                                                       NULL);
+                                                                   0, NULL);
 
        bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT);
        database->cli_feat = gatt_db_service_add_characteristic(service,
@@ -2037,11 +2043,18 @@ 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,
+                                       uint32_t *ccc_perm,
                                        bool *req_prep_authorization)
 {
        const char *flag;
 
-       *props = *ext_props = 0;
+       if (!props || !ext_props || !perm || !ccc_perm)
+               return false;
+
+       *props = 0;
+       *ext_props = 0;
+       *perm = 0;
+       *ccc_perm = 0;
 
        do {
                if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_STRING)
@@ -2094,6 +2107,24 @@ static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props,
                        *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE;
                } else if (!strcmp("authorize", flag)) {
                        *req_prep_authorization = true;
+               } else if (!strcmp("encrypt-notify", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_ENCRYPT;
+                       *props |= BT_GATT_CHRC_PROP_NOTIFY;
+               } else if (!strcmp("encrypt-authenticated-notify", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN;
+                       *props |= BT_GATT_CHRC_PROP_NOTIFY;
+               } else if (!strcmp("secure-notify", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_SECURE;
+                       *props |= BT_GATT_CHRC_PROP_NOTIFY;
+               } else if (!strcmp("encrypt-indicate", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_ENCRYPT;
+                       *props |= BT_GATT_CHRC_PROP_INDICATE;
+               } else if (!strcmp("encrypt-authenticated-indicate", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_AUTHEN;
+                       *props |= BT_GATT_CHRC_PROP_INDICATE;
+               } else if (!strcmp("secure-indicate", flag)) {
+                       *ccc_perm |= BT_ATT_PERM_WRITE_SECURE;
+                       *props |= BT_GATT_CHRC_PROP_INDICATE;
                } else {
                        error("Invalid characteristic flag: %s", flag);
                        return false;
@@ -2147,7 +2178,8 @@ 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, bool *req_prep_authorization)
+                                           uint32_t *perm, uint32_t *ccc_perm,
+                                           bool *req_prep_authorization)
 {
        DBusMessageIter iter, array;
        const char *iface;
@@ -2164,7 +2196,7 @@ static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props,
        if (!strcmp(iface, GATT_DESC_IFACE))
                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, ccc_perm,
                                                        req_prep_authorization);
 }
 
@@ -2214,7 +2246,7 @@ static struct external_chrc *chrc_create(struct gatt_app *app,
         * created.
         */
        if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm,
-                                       &chrc->req_prep_authorization)) {
+                       &chrc->ccc_perm, &chrc->req_prep_authorization)) {
                error("Failed to parse characteristic properties");
                goto fail;
        }
@@ -2296,7 +2328,7 @@ 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, NULL,
                                        &desc->req_prep_authorization)) {
                error("Failed to parse characteristic properties");
                goto fail;
@@ -3457,7 +3489,7 @@ static bool database_add_ccc(struct external_service *service,
                return true;
 
        chrc->ccc = service_add_ccc(service->attrib, service->app->database,
-                                               ccc_write_cb, chrc, NULL);
+                                   ccc_write_cb, chrc, chrc->ccc_perm, NULL);
        if (!chrc->ccc) {
                error("Failed to create CCC entry for characteristic");
                return false;