u8 svc_hint;
 };
 
+struct blocked_key {
+       struct list_head list;
+       struct rcu_head rcu;
+       u8 type;
+       u8 val[16];
+};
+
 struct smp_csrk {
        bdaddr_t bdaddr;
        u8 bdaddr_type;
        struct list_head        le_conn_params;
        struct list_head        pend_le_conns;
        struct list_head        pend_le_reports;
+       struct list_head        blocked_keys;
 
        struct hci_dev_stats    stat;
 
 struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                            u8 addr_type, u8 val[16], bdaddr_t *rpa);
 void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
+bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16]);
+void hci_blocked_keys_clear(struct hci_dev *hdev);
 void hci_smp_irks_clear(struct hci_dev *hdev);
 
 bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 
 } __packed;
 #define MGMT_SET_PHY_CONFIGURATION_SIZE        4
 
+#define MGMT_OP_SET_BLOCKED_KEYS       0x0046
+
+#define HCI_BLOCKED_KEY_TYPE_LINKKEY   0x00
+#define HCI_BLOCKED_KEY_TYPE_LTK       0x01
+#define HCI_BLOCKED_KEY_TYPE_IRK       0x02
+
+struct mgmt_blocked_key_info {
+       __u8 type;
+       __u8 val[16];
+} __packed;
+
+struct mgmt_cp_set_blocked_keys {
+       __le16 key_count;
+       struct mgmt_blocked_key_info keys[0];
+} __packed;
+#define MGMT_OP_SET_BLOCKED_KEYS_SIZE 2
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
 
        }
 }
 
+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;
        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;
                }
        }
 
                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;
                }
        }
 
 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 */
        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,
        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);
        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);
 
 
 DEFINE_SHOW_ATTRIBUTE(blacklist);
 
+static int blocked_keys_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct blocked_key *key;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(key, &hdev->blocked_keys, list)
+               seq_printf(f, "%u %*phN\n", key->type, 16, key->val);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(blocked_keys);
+
 static int uuids_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
                            &device_list_fops);
        debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
                            &blacklist_fops);
+       debugfs_create_file("blocked_keys", 0444, hdev->debugfs, hdev,
+                           &blocked_keys_fops);
        debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
        debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
                            &remote_oob_fops);
 
        MGMT_OP_START_LIMITED_DISCOVERY,
        MGMT_OP_READ_EXT_INFO,
        MGMT_OP_SET_APPEARANCE,
+       MGMT_OP_SET_BLOCKED_KEYS,
 };
 
 static const u16 mgmt_events[] = {
        for (i = 0; i < key_count; i++) {
                struct mgmt_link_key_info *key = &cp->keys[i];
 
+               if (hci_is_blocked_key(hdev,
+                                      HCI_BLOCKED_KEY_TYPE_LINKKEY,
+                                      key->val)) {
+                       bt_dev_warn(hdev, "Skipping blocked link key for %pMR",
+                                   &key->addr.bdaddr);
+                       continue;
+               }
+
                /* Always ignore debug keys and require a new pairing if
                 * the user wants to use them.
                 */
        return err;
 }
 
+static int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data,
+                           u16 len)
+{
+       int err = MGMT_STATUS_SUCCESS;
+       struct mgmt_cp_set_blocked_keys *keys = data;
+       const u16 max_key_count = ((U16_MAX - sizeof(*keys)) /
+                                  sizeof(struct mgmt_blocked_key_info));
+       u16 key_count, expected_len;
+       int i;
+
+       BT_DBG("request for %s", hdev->name);
+
+       key_count = __le16_to_cpu(keys->key_count);
+       if (key_count > max_key_count) {
+               bt_dev_err(hdev, "too big key_count value %u", key_count);
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
+                                      MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       expected_len = struct_size(keys, keys, key_count);
+       if (expected_len != len) {
+               bt_dev_err(hdev, "expected %u bytes, got %u bytes",
+                          expected_len, len);
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
+                                      MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       hci_dev_lock(hdev);
+
+       hci_blocked_keys_clear(hdev);
+
+       for (i = 0; i < keys->key_count; ++i) {
+               struct blocked_key *b = kzalloc(sizeof(*b), GFP_KERNEL);
+
+               if (!b) {
+                       err = MGMT_STATUS_NO_RESOURCES;
+                       break;
+               }
+
+               b->type = keys->keys[i].type;
+               memcpy(b->val, keys->keys[i].val, sizeof(b->val));
+               list_add_rcu(&b->list, &hdev->blocked_keys);
+       }
+       hci_dev_unlock(hdev);
+
+       return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
+                               err, NULL, 0);
+}
+
 static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
                                         u16 opcode, struct sk_buff *skb)
 {
        for (i = 0; i < irk_count; i++) {
                struct mgmt_irk_info *irk = &cp->irks[i];
 
+               if (hci_is_blocked_key(hdev,
+                                      HCI_BLOCKED_KEY_TYPE_IRK,
+                                      irk->val)) {
+                       bt_dev_warn(hdev, "Skipping blocked IRK for %pMR",
+                                   &irk->addr.bdaddr);
+                       continue;
+               }
+
                hci_add_irk(hdev, &irk->addr.bdaddr,
                            le_addr_type(irk->addr.type), irk->val,
                            BDADDR_ANY);
                struct mgmt_ltk_info *key = &cp->keys[i];
                u8 type, authenticated;
 
+               if (hci_is_blocked_key(hdev,
+                                      HCI_BLOCKED_KEY_TYPE_LTK,
+                                      key->val)) {
+                       bt_dev_warn(hdev, "Skipping blocked LTK for %pMR",
+                                   &key->addr.bdaddr);
+                       continue;
+               }
+
                switch (key->type) {
                case MGMT_LTK_UNAUTHENTICATED:
                        authenticated = 0x00;
        { set_appearance,          MGMT_SET_APPEARANCE_SIZE },
        { get_phy_configuration,   MGMT_GET_PHY_CONFIGURATION_SIZE },
        { set_phy_configuration,   MGMT_SET_PHY_CONFIGURATION_SIZE },
+       { set_blocked_keys,        MGMT_OP_SET_BLOCKED_KEYS_SIZE,
+                                               HCI_MGMT_VAR_LEN },
 };
 
 void mgmt_index_added(struct hci_dev *hdev)
 
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
+       /* Pairing is aborted if any blocked keys are distributed */
+       if (hci_is_blocked_key(conn->hcon->hdev, HCI_BLOCKED_KEY_TYPE_LTK,
+                              rp->ltk)) {
+               bt_dev_warn_ratelimited(conn->hcon->hdev,
+                                       "LTK blocked for %pMR",
+                                       &conn->hcon->dst);
+               return SMP_INVALID_PARAMS;
+       }
+
        SMP_ALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
 
        skb_pull(skb, sizeof(*rp));
        if (skb->len < sizeof(*info))
                return SMP_INVALID_PARAMS;
 
+       /* Pairing is aborted if any blocked keys are distributed */
+       if (hci_is_blocked_key(conn->hcon->hdev, HCI_BLOCKED_KEY_TYPE_IRK,
+                              info->irk)) {
+               bt_dev_warn_ratelimited(conn->hcon->hdev,
+                                       "Identity key blocked for %pMR",
+                                       &conn->hcon->dst);
+               return SMP_INVALID_PARAMS;
+       }
+
        SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
 
        skb_pull(skb, sizeof(*info));