Bluetooth: Implementation of MGMT_OP_SET_BLOCKED_KEYS.
[sfrench/cifs-2.6.git] / net / bluetooth / hci_core.c
index 9e19d5a3aac87086e0e10fc0785eeb64d59512eb..f0298db26dc351d3fd23de34a01b496673cdbb57 100644 (file)
@@ -2311,6 +2311,33 @@ void hci_smp_irks_clear(struct hci_dev *hdev)
        }
 }
 
+void hci_blocked_keys_clear(struct hci_dev *hdev)
+{
+       struct blocked_key *b;
+
+       list_for_each_entry_rcu(b, &hdev->blocked_keys, list) {
+               list_del_rcu(&b->list);
+               kfree_rcu(b, rcu);
+       }
+}
+
+bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16])
+{
+       bool blocked = false;
+       struct blocked_key *b;
+
+       rcu_read_lock();
+       list_for_each_entry(b, &hdev->blocked_keys, list) {
+               if (b->type == type && !memcmp(b->val, val, sizeof(b->val))) {
+                       blocked = true;
+                       break;
+               }
+       }
+
+       rcu_read_unlock();
+       return blocked;
+}
+
 struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 {
        struct link_key *k;
@@ -2319,6 +2346,16 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
        list_for_each_entry_rcu(k, &hdev->link_keys, list) {
                if (bacmp(bdaddr, &k->bdaddr) == 0) {
                        rcu_read_unlock();
+
+                       if (hci_is_blocked_key(hdev,
+                                              HCI_BLOCKED_KEY_TYPE_LINKKEY,
+                                              k->val)) {
+                               bt_dev_warn_ratelimited(hdev,
+                                                       "Link key blocked for %pMR",
+                                                       &k->bdaddr);
+                               return NULL;
+                       }
+
                        return k;
                }
        }
@@ -2387,6 +2424,15 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
 
                if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) {
                        rcu_read_unlock();
+
+                       if (hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_LTK,
+                                              k->val)) {
+                               bt_dev_warn_ratelimited(hdev,
+                                                       "LTK blocked for %pMR",
+                                                       &k->bdaddr);
+                               return NULL;
+                       }
+
                        return k;
                }
        }
@@ -2397,31 +2443,42 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
 
 struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
 {
+       struct smp_irk *irk_to_return = NULL;
        struct smp_irk *irk;
 
        rcu_read_lock();
        list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                if (!bacmp(&irk->rpa, rpa)) {
-                       rcu_read_unlock();
-                       return irk;
+                       irk_to_return = irk;
+                       goto done;
                }
        }
 
        list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                if (smp_irk_matches(hdev, irk->val, rpa)) {
                        bacpy(&irk->rpa, rpa);
-                       rcu_read_unlock();
-                       return irk;
+                       irk_to_return = irk;
+                       goto done;
                }
        }
+
+done:
+       if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
+                                               irk_to_return->val)) {
+               bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
+                                       &irk_to_return->bdaddr);
+               irk_to_return = NULL;
+       }
+
        rcu_read_unlock();
 
-       return NULL;
+       return irk_to_return;
 }
 
 struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                     u8 addr_type)
 {
+       struct smp_irk *irk_to_return = NULL;
        struct smp_irk *irk;
 
        /* Identity Address must be public or static random */
@@ -2432,13 +2489,23 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
        list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                if (addr_type == irk->addr_type &&
                    bacmp(bdaddr, &irk->bdaddr) == 0) {
-                       rcu_read_unlock();
-                       return irk;
+                       irk_to_return = irk;
+                       goto done;
                }
        }
+
+done:
+
+       if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
+                                               irk_to_return->val)) {
+               bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
+                                       &irk_to_return->bdaddr);
+               irk_to_return = NULL;
+       }
+
        rcu_read_unlock();
 
-       return NULL;
+       return irk_to_return;
 }
 
 struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
@@ -3244,6 +3311,7 @@ struct hci_dev *hci_alloc_dev(void)
        INIT_LIST_HEAD(&hdev->pend_le_reports);
        INIT_LIST_HEAD(&hdev->conn_hash.list);
        INIT_LIST_HEAD(&hdev->adv_instances);
+       INIT_LIST_HEAD(&hdev->blocked_keys);
 
        INIT_WORK(&hdev->rx_work, hci_rx_work);
        INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -3443,6 +3511,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
        hci_bdaddr_list_clear(&hdev->le_resolv_list);
        hci_conn_params_clear_all(hdev);
        hci_discovery_filter_clear(hdev);
+       hci_blocked_keys_clear(hdev);
        hci_dev_unlock(hdev);
 
        hci_dev_put(hdev);