diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 135dfac1be12d90bad8d2f7837c8d0478439cf41..3a3f7b45380394983ad780f510164c9cf5e6154e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -515,8 +515,8 @@ int hci_uuids_clear(struct hci_dev *hdev); int hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 *key, u8 type, u8 pin_len); +int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_remote_oob_data_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 07d0ba35b9a5fbd8b1ed1491edfe5bea0382f2ae..5f55aef63e20085238ead692b91970b6c0584219 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1022,8 +1022,44 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) return NULL; } -int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 *val, u8 type, u8 pin_len) +static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, + u8 key_type, u8 old_key_type) +{ + /* Legacy key */ + if (key_type < 0x03) + return 1; + + /* Debug keys are insecure so don't store them persistently */ + if (key_type == HCI_LK_DEBUG_COMBINATION) + return 0; + + /* Changed combination key and there's no previous one */ + if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) + return 0; + + /* Security mode 3 case */ + if (!conn) + return 1; + + /* Neither local nor remote side had no-bonding as requirement */ + if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) + return 1; + + /* Local side had dedicated bonding as requirement */ + if (conn->auth_type == 0x02 || conn->auth_type == 0x03) + return 1; + + /* Remote side had dedicated bonding as requirement */ + if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) + return 1; + + /* If none of the above criteria match, then don't store the key + * persistently */ + return 0; +} + +int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, + bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { struct link_key *key, *old_key; u8 old_key_type; @@ -1042,6 +1078,20 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); + /* Some buggy controller combinations generate a changed + * combination key for legacy pairing even when there's no + * previous key */ + if (type == HCI_LK_CHANGED_COMBINATION && + (!conn || conn->remote_auth == 0xff) && + old_key_type == 0xff) + type = HCI_LK_COMBINATION; + + if (new_key && !hci_persistent_key(hdev, conn, type, old_key_type)) { + list_del(&key->list); + kfree(key); + return 0; + } + bacpy(&key->bdaddr, bdaddr); memcpy(key->val, val, 16); key->type = type; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ebbaa6c8d0158528bf2188795bd0fb2c37d9d45b..8a63d3a463f770f615e53394b5446bb7d2cf1dec 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2136,7 +2136,7 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff } if (test_bit(HCI_LINK_KEYS, &hdev->flags)) - hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key, + hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, ev->key_type, pin_len); hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a1b0ec4e5178610375c58d15bfba9a860aabcd11..e1384fc6016ca87fa2216af112ccd36f32e2f550 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -945,7 +945,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) for (i = 0; i < key_count; i++) { struct mgmt_key_info *key = &cp->keys[i]; - hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, + hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); }