Add support for requiring min key size for access GATT characteristics 60/204960/1
authorSzymon Janc <szymon.janc@codecoup.pl>
Fri, 23 Mar 2018 13:08:21 +0000 (14:08 +0100)
committerAmit Purwar <amit.purwar@samsung.com>
Fri, 26 Apr 2019 10:54:22 +0000 (16:24 +0530)
This allows to configure bluetoothd to require minimum encryption key
size when accessing GATT server characteristics. It is a global
configuration option affecting whole GATT database.

Change-Id: Ibec567f17447d35421f016c5e0021ef49aa713ef
Signed-off-by: Amit Purwar <amit.purwar@samsung.com>
14 files changed:
peripheral/gatt.c
src/adapter.c
src/device.c
src/device.h
src/hcid.h
src/main.c
src/main.conf
src/shared/att.c
src/shared/att.h
src/shared/gatt-client.c
src/shared/gatt-server.c
src/shared/gatt-server.h
tools/btgatt-server.c
unit/test-gatt.c

index 5ae19a8..08541c4 100755 (executable)
@@ -128,7 +128,7 @@ static struct gatt_conn *gatt_conn_new(int fd)
 
        bt_att_set_security(conn->att, BT_SECURITY_MEDIUM);
 
-       conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu);
+       conn->gatt = bt_gatt_server_new(gatt_db, conn->att, mtu, 0);
        if (!conn->gatt) {
                fprintf(stderr, "Failed to create GATT server\n");
                bt_att_unref(conn->att);
index 49cbfc3..2d56391 100644 (file)
@@ -7686,25 +7686,27 @@ failed:
        return ltk;
 }
 
-static GSList *get_ltk_info(GKeyFile *key_file, const char *peer,
+static struct smp_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer,
+                                                       uint8_t bdaddr_type)
+{
+       DBG("%s", peer);
+
+       return get_ltk(key_file, peer, bdaddr_type, "LongTermKey");
+}
+
+static struct smp_ltk_info *get_slave_ltk_info(GKeyFile *key_file,
+                                                       const char *peer,
                                                        uint8_t bdaddr_type)
 {
        struct smp_ltk_info *ltk;
-       GSList *l = NULL;
 
        DBG("%s", peer);
 
-       ltk = get_ltk(key_file, peer, bdaddr_type, "LongTermKey");
-       if (ltk)
-               l = g_slist_append(l, ltk);
-
        ltk = get_ltk(key_file, peer, bdaddr_type, "SlaveLongTermKey");
-       if (ltk) {
+       if (ltk)
                ltk->master = false;
-               l = g_slist_append(l, ltk);
-       }
 
-       return l;
+       return ltk;
 }
 
 static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
@@ -8318,11 +8320,15 @@ static void load_devices(struct btd_adapter *adapter)
                GKeyFile *key_file;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                struct link_key_info *key_info = NULL;
-               GSList *list, *ltk_info = NULL;
+               struct smp_ltk_info *ltk_info = NULL;
+               struct smp_ltk_info *slave_ltk_info = NULL;
+               GSList *list = NULL;
                struct device_addr_type addr;
 #else
                struct link_key_info *key_info;
-               GSList *list, *ltk_info;
+               struct smp_ltk_info *ltk_info;
+               struct smp_ltk_info *slave_ltk_info;
+               GSList *list;
 #endif
                struct irk_info *irk_info;
                struct conn_param *param;
@@ -8360,7 +8366,13 @@ static void load_devices(struct btd_adapter *adapter)
                bdaddr_type = get_le_addr_type(key_file);
 
                ltk_info = get_ltk_info(key_file, entry->d_name, bdaddr_type);
-               ltks = g_slist_concat(ltks, ltk_info);
+               if (ltk_info)
+                       ltks = g_slist_append(ltks, ltk_info);
+
+               slave_ltk_info = get_slave_ltk_info(key_file, entry->d_name,
+                                                               bdaddr_type);
+               if (slave_ltk_info)
+                       ltks = g_slist_append(ltks, slave_ltk_info);
 
                irk_info = get_irk_info(key_file, entry->d_name, bdaddr_type);
                if (irk_info)
@@ -8420,7 +8432,8 @@ static void load_devices(struct btd_adapter *adapter)
                                ltks = g_slist_remove(ltks, ltk_info);
                                ltk_info = get_ltk_info(key_file,
                                                idaddr, bdaddr_type);
-                               ltks = g_slist_concat(ltks, ltk_info);
+                               if (ltk_info)
+                                       ltks = g_slist_append(ltks, ltk_info);
                        }
 
                        if (irk_info) {
@@ -8447,9 +8460,16 @@ device_exist:
                        device_set_bonded(device, BDADDR_BREDR);
                }
 
-               if (ltk_info) {
+               if (ltk_info || slave_ltk_info) {
                        device_set_paired(device, bdaddr_type);
                        device_set_bonded(device, bdaddr_type);
+
+                       if (ltk_info)
+                               device_set_ltk_enc_size(device,
+                                                       ltk_info->enc_size);
+                       else if (slave_ltk_info)
+                               device_set_ltk_enc_size(device,
+                                               slave_ltk_info->enc_size);
                }
 
 free:
@@ -12690,6 +12710,8 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
                device_set_bonded(device, addr->type);
        }
 
+       device_set_ltk_enc_size(device, ev->key.enc_size);
+
        bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
 }
 
index 5321f6e..7c9736f 100644 (file)
@@ -286,6 +286,7 @@ struct btd_device {
 
        struct csrk_info *local_csrk;
        struct csrk_info *remote_csrk;
+       uint8_t ltk_enc_size;
 
        sdp_list_t      *tmp_records;
 
@@ -1944,6 +1945,12 @@ bool device_is_disconnecting(struct btd_device *device)
        return device->disconn_timer > 0;
 }
 
+void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size)
+{
+       device->ltk_enc_size = enc_size;
+       bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
+}
+
 static void device_set_auto_connect(struct btd_device *device, gboolean enable)
 {
        char addr[18];
@@ -7066,9 +7073,14 @@ static void gatt_server_init(struct btd_device *device,
 
        gatt_server_cleanup(device);
 
-       device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
-       if (!device->server)
+       device->server = bt_gatt_server_new(db, device->att, device->att_mtu,
+                                               main_opts.min_enc_key_size);
+       if (!device->server) {
                error("Failed to initialize bt_gatt_server");
+               return;
+       }
+       bt_att_set_enc_key_size(device->att, device->ltk_enc_size);
 
        bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
 
index 4f62671..5a7b57e 100644 (file)
@@ -165,6 +165,7 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type);
 void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type);
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
 bool device_is_disconnecting(struct btd_device *device);
+void device_set_ltk_enc_size(struct btd_device *device, uint8_t enc_size);
 
 typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
                                        void *user_data);
index c71a1e3..9243285 100755 (executable)
@@ -59,6 +59,8 @@ struct main_opts {
 
        bt_mode_t       mode;
        bt_gatt_cache_t gatt_cache;
+
+       uint8_t         min_enc_key_size;
 };
 
 extern struct main_opts main_opts;
index bb52422..2b36245 100755 (executable)
@@ -108,6 +108,7 @@ static const char *policy_options[] = {
 
 static const char *gatt_options[] = {
        "Cache",
+       "MinEncKeySize",
        NULL
 };
 
@@ -433,12 +434,22 @@ static void parse_config(GKeyFile *config)
        if (err) {
                g_clear_error(&err);
                main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS;
-               return;
+       } else {
+               main_opts.gatt_cache = parse_gatt_cache(str);
+               g_free(str);
        }
 
-       main_opts.gatt_cache = parse_gatt_cache(str);
+       val = g_key_file_get_integer(config, "GATT",
+                                               "MinEncKeySize", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("MinEncKeySize=%d", val);
 
-       g_free(str);
+               if (val >=7 && val <= 16)
+                       main_opts.min_enc_key_size = val;
+       }
 }
 
 static void init_defaults(void)
index ba17405..de18a58 100755 (executable)
 # Default: always
 #Cache = always
 
+# Minimum required Encryption Key Size for accessing secured characteristics.
+# Possible values: 0 and 7-16. 0 means don't care.
+# Defaults to 0
+# MinEncKeySize = 0
+
 [Policy]
 #
 # The ReconnectUUIDs defines the set of remote services that should try
index 653f145..46b7d81 100755 (executable)
@@ -55,6 +55,7 @@ struct bt_att {
        struct io *io;
        bool io_on_l2cap;
        int io_sec_level;               /* Only used for non-L2CAP */
+       uint8_t enc_size;
 
        struct queue *req_queue;        /* Queued ATT protocol requests */
        struct att_send_op *pending_req;
@@ -624,7 +625,7 @@ static bool change_security(struct bt_att *att, uint8_t ecode)
        if (att->io_sec_level != BT_ATT_SECURITY_AUTO)
                return false;
 
-       security = bt_att_get_security(att);
+       security = bt_att_get_security(att, NULL);
 
        if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
                                        security < BT_ATT_SECURITY_MEDIUM) {
@@ -1476,7 +1477,7 @@ bool bt_att_unregister_all(struct bt_att *att)
        return true;
 }
 
-int bt_att_get_security(struct bt_att *att)
+int bt_att_get_security(struct bt_att *att, uint8_t *enc_size)
 {
        struct bt_security sec;
        socklen_t len;
@@ -1484,14 +1485,21 @@ int bt_att_get_security(struct bt_att *att)
        if (!att)
                return -EINVAL;
 
-       if (!att->io_on_l2cap)
+       if (!att->io_on_l2cap) {
+               if (enc_size)
+                       *enc_size = att->enc_size;
+
                return att->io_sec_level;
+       }
 
        memset(&sec, 0, sizeof(sec));
        len = sizeof(sec);
        if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
                return -EIO;
 
+       if (enc_size)
+               *enc_size = att->enc_size;
+
        return sec.level;
 }
 
@@ -1518,6 +1526,14 @@ bool bt_att_set_security(struct bt_att *att, int level)
        return true;
 }
 
+void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size)
+{
+       if (!att)
+               return;
+
+       att->enc_size = enc_size;
+}
+
 static bool sign_set_key(struct sign_info **sign, uint8_t key[16],
                                bt_att_counter_func_t func, void *user_data)
 {
index d05e720..e6cc8ed 100755 (executable)
@@ -87,8 +87,9 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id);
 
 bool bt_att_unregister_all(struct bt_att *att);
 
-int bt_att_get_security(struct bt_att *att);
+int bt_att_get_security(struct bt_att *att, uint8_t *enc_size);
 bool bt_att_set_security(struct bt_att *att, int level);
+void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size);
 
 bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
                        bt_att_counter_func_t func, void *user_data);
index 0c24a6f..d665cf3 100644 (file)
@@ -2886,7 +2886,7 @@ unsigned int bt_gatt_client_write_without_response_async(
 
        /* Only use signed write if unencrypted */
        if (signed_write) {
-               security = bt_att_get_security(client->att);
+               security = bt_att_get_security(client->att, NULL);
                opcode = security > BT_SECURITY_LOW ?  BT_ATT_OP_WRITE_CMD :
                                                BT_ATT_OP_SIGNED_WRITE_CMD;
        } else
@@ -2926,7 +2926,7 @@ unsigned int bt_gatt_client_write_without_response(
 
        /* Only use signed write if unencrypted */
        if (signed_write) {
-               security = bt_att_get_security(client->att);
+               security = bt_att_get_security(client->att, NULL);
                op = security > BT_SECURITY_LOW ?  BT_ATT_OP_WRITE_CMD :
                                                BT_ATT_OP_SIGNED_WRITE_CMD;
        } else
@@ -3648,7 +3648,7 @@ int bt_gatt_client_get_security(struct bt_gatt_client *client)
        if (!client)
                return -1;
 
-       return bt_att_get_security(client->att);
+       return bt_att_get_security(client->att, NULL);
 }
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
index d22e06b..b0e9679 100755 (executable)
@@ -103,6 +103,8 @@ struct bt_gatt_server {
        unsigned int prep_write_id;
        unsigned int exec_write_id;
 
+       uint8_t min_enc_size;
+
        struct queue *prep_queue;
        unsigned int max_prep_queue_len;
 
@@ -389,9 +391,18 @@ done:
        process_read_by_type(op);
 }
 
+static bool check_min_key_size(uint8_t min_size, uint8_t size)
+{
+       if (!min_size || !size)
+               return true;
+
+       return min_size <= size;
+}
+
 static uint8_t check_permissions(struct bt_gatt_server *server,
                                struct gatt_db_attribute *attr, uint32_t mask)
 {
+       uint8_t enc_size;
        uint32_t perm;
        int security;
 
@@ -407,15 +418,33 @@ static uint8_t check_permissions(struct bt_gatt_server *server,
        if (!perm)
                return 0;
 
-       security = bt_att_get_security(server->att);
-       if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS)
-               return BT_ATT_ERROR_AUTHENTICATION;
+       security = bt_att_get_security(server->att, &enc_size);
+       if (security < 0)
+               return BT_ATT_ERROR_UNLIKELY;
 
-       if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH)
-               return BT_ATT_ERROR_AUTHENTICATION;
+       if (perm & BT_ATT_PERM_SECURE) {
+               if (security < BT_ATT_SECURITY_FIPS)
+                       return BT_ATT_ERROR_AUTHENTICATION;
 
-       if (perm & BT_ATT_PERM_ENCRYPT && security < BT_ATT_SECURITY_MEDIUM)
-               return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION;
+               if (!check_min_key_size(server->min_enc_size, enc_size))
+                       return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE;
+       }
+
+       if (perm & BT_ATT_PERM_AUTHEN) {
+               if (security < BT_ATT_SECURITY_HIGH)
+                       return BT_ATT_ERROR_AUTHENTICATION;
+
+               if (!check_min_key_size(server->min_enc_size, enc_size))
+                       return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE;
+       }
+
+       if (perm & BT_ATT_PERM_ENCRYPT) {
+               if (security < BT_ATT_SECURITY_MEDIUM)
+                       return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION;
+
+               if (!check_min_key_size(server->min_enc_size, enc_size))
+                       return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE;
+       }
 
        return 0;
 }
@@ -1536,7 +1565,8 @@ static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
 }
 
 struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
-                                       struct bt_att *att, uint16_t mtu)
+                                       struct bt_att *att, uint16_t mtu,
+                                       uint8_t min_enc_size)
 {
        struct bt_gatt_server *server;
 
@@ -1549,6 +1579,7 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
        server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);
        server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN;
        server->prep_queue = queue_new();
+       server->min_enc_size = min_enc_size;
 
        if (!gatt_server_register_att_handlers(server)) {
                bt_gatt_server_free(server);
index 4e84fa7..b2c2b06 100755 (executable)
@@ -31,7 +31,8 @@ struct bt_att;
 struct bt_gatt_server;
 
 struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
-                                       struct bt_att *att, uint16_t mtu);
+                                       struct bt_att *att, uint16_t mtu,
+                                       uint8_t min_enc_size);
 uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server);
 
 struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
index fadaff2..89812bd 100755 (executable)
@@ -583,7 +583,7 @@ static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
                goto fail;
        }
 
-       server->gatt = bt_gatt_server_new(server->db, server->att, mtu);
+       server->gatt = bt_gatt_server_new(server->db, server->att, mtu, 0);
        if (!server->gatt) {
                fprintf(stderr, "Failed to create GATT server\n");
                goto fail;
index 76d431f..5961b02 100755 (executable)
@@ -792,7 +792,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
                g_assert(context->server_db);
 
                context->server = bt_gatt_server_new(context->server_db,
-                                                       context->att, mtu);
+                                                       context->att, mtu, 0);
                g_assert(context->server);
 
                bt_gatt_server_set_debug(context->server, print_debug,