Bluetooth: Fix link key persistent storage criteria
authorJohan Hedberg <johan.hedberg@nokia.com>
Thu, 28 Apr 2011 18:28:59 +0000 (11:28 -0700)
committerGustavo F. Padovan <padovan@profusion.mobi>
Thu, 28 Apr 2011 19:14:40 +0000 (16:14 -0300)
Link keys should only be stored if very specific criteria of the
authentication process are fulfilled. This patch essentially copies the
criteria that user space has so far been using to the kernel side so
that the management interface works properly.

Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index 135dfac1be12d90bad8d2f7837c8d0478439cf41..3a3f7b45380394983ad780f510164c9cf5e6154e 100644 (file)
@@ -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);
index 07d0ba35b9a5fbd8b1ed1491edfe5bea0382f2ae..5f55aef63e20085238ead692b91970b6c0584219 100644 (file)
@@ -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;
index ebbaa6c8d0158528bf2188795bd0fb2c37d9d45b..8a63d3a463f770f615e53394b5446bb7d2cf1dec 100644 (file)
@@ -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);
index a1b0ec4e5178610375c58d15bfba9a860aabcd11..e1384fc6016ca87fa2216af112ccd36f32e2f550 100644 (file)
@@ -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);
        }