]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/bluetooth/hci_event.c
Bluetooth: Rename hci_find_ltk_by_addr to hci_find_ltk
[mirror_ubuntu-artful-kernel.git] / net / bluetooth / hci_event.c
index aa152140c3e279bd6599b5b09498f748e501c08e..34ecbf0b7e5b8d88855a933053c95508fe36cd86 100644 (file)
@@ -189,6 +189,9 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
 
        clear_bit(HCI_RESET, &hdev->flags);
 
+       if (status)
+               return;
+
        /* Reset all non-persistent flags */
        hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
 
@@ -991,8 +994,8 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        hci_dev_lock(hdev);
-       mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
-                                         NULL, NULL, rp->status);
+       mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+                                         rp->status);
        hci_dev_unlock(hdev);
 }
 
@@ -1004,8 +1007,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        hci_dev_lock(hdev);
-       mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
-                                         rp->hash256, rp->randomizer256,
+       mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+                                         rp->hash256, rp->rand256,
                                          rp->status);
        hci_dev_unlock(hdev);
 }
@@ -1578,7 +1581,14 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
        struct discovery_state *discov = &hdev->discovery;
        struct inquiry_entry *e;
 
-       if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+       /* Update the mgmt connected state if necessary. Be careful with
+        * conn objects that exist but are not (yet) connected however.
+        * Only those in BT_CONFIG or BT_CONNECTED states can be
+        * considered connected.
+        */
+       if (conn &&
+           (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+           !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
                mgmt_device_connected(hdev, conn, 0, name, name_len);
 
        if (discov->state == DISCOVERY_STOPPED)
@@ -1944,6 +1954,29 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+       struct hci_cp_switch_role *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+       if (conn)
+               clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
@@ -2847,6 +2880,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_create_conn(hdev, ev->status);
                break;
 
+       case HCI_OP_DISCONNECT:
+               hci_cs_disconnect(hdev, ev->status);
+               break;
+
        case HCI_OP_ADD_SCO:
                hci_cs_add_sco(hdev, ev->status);
                break;
@@ -2875,24 +2912,24 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cs_setup_sync_conn(hdev, ev->status);
                break;
 
-       case HCI_OP_SNIFF_MODE:
-               hci_cs_sniff_mode(hdev, ev->status);
+       case HCI_OP_CREATE_PHY_LINK:
+               hci_cs_create_phylink(hdev, ev->status);
                break;
 
-       case HCI_OP_EXIT_SNIFF_MODE:
-               hci_cs_exit_sniff_mode(hdev, ev->status);
+       case HCI_OP_ACCEPT_PHY_LINK:
+               hci_cs_accept_phylink(hdev, ev->status);
                break;
 
-       case HCI_OP_DISCONNECT:
-               hci_cs_disconnect(hdev, ev->status);
+       case HCI_OP_SNIFF_MODE:
+               hci_cs_sniff_mode(hdev, ev->status);
                break;
 
-       case HCI_OP_CREATE_PHY_LINK:
-               hci_cs_create_phylink(hdev, ev->status);
+       case HCI_OP_EXIT_SNIFF_MODE:
+               hci_cs_exit_sniff_mode(hdev, ev->status);
                break;
 
-       case HCI_OP_ACCEPT_PHY_LINK:
-               hci_cs_accept_phylink(hdev, ev->status);
+       case HCI_OP_SWITCH_ROLE:
+               hci_cs_switch_role(hdev, ev->status);
                break;
 
        case HCI_OP_LE_CREATE_CONN:
@@ -2922,6 +2959,13 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+       BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+}
+
 static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_role_change *ev = (void *) skb->data;
@@ -3147,6 +3191,38 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+       if (key_type == HCI_LK_CHANGED_COMBINATION)
+               return;
+
+       conn->pin_length = pin_len;
+       conn->key_type = key_type;
+
+       switch (key_type) {
+       case HCI_LK_LOCAL_UNIT:
+       case HCI_LK_REMOTE_UNIT:
+       case HCI_LK_DEBUG_COMBINATION:
+               return;
+       case HCI_LK_COMBINATION:
+               if (pin_len == 16)
+                       conn->pending_sec_level = BT_SECURITY_HIGH;
+               else
+                       conn->pending_sec_level = BT_SECURITY_MEDIUM;
+               break;
+       case HCI_LK_UNAUTH_COMBINATION_P192:
+       case HCI_LK_UNAUTH_COMBINATION_P256:
+               conn->pending_sec_level = BT_SECURITY_MEDIUM;
+               break;
+       case HCI_LK_AUTH_COMBINATION_P192:
+               conn->pending_sec_level = BT_SECURITY_HIGH;
+               break;
+       case HCI_LK_AUTH_COMBINATION_P256:
+               conn->pending_sec_level = BT_SECURITY_FIPS;
+               break;
+       }
+}
+
 static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_link_key_req *ev = (void *) skb->data;
@@ -3188,8 +3264,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                        goto not_found;
                }
 
-               conn->key_type = key->type;
-               conn->pin_length = key->pin_len;
+               conn_set_key(conn, key->type, key->pin_len);
        }
 
        bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -3219,16 +3294,14 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
-       if (conn) {
-               hci_conn_hold(conn);
-               conn->disc_timeout = HCI_DISCONN_TIMEOUT;
-               pin_len = conn->pin_length;
+       if (!conn)
+               goto unlock;
 
-               if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
-                       conn->key_type = ev->key_type;
+       hci_conn_hold(conn);
+       conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+       hci_conn_drop(conn);
 
-               hci_conn_drop(conn);
-       }
+       conn_set_key(conn, ev->key_type, conn->pin_length);
 
        if (!test_bit(HCI_MGMT, &hdev->dev_flags))
                goto unlock;
@@ -3238,6 +3311,12 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (!key)
                goto unlock;
 
+       /* Update connection information since adding the key will have
+        * fixed up the type in the case of changed combination keys.
+        */
+       if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+               conn_set_key(conn, key->type, key->pin_len);
+
        mgmt_new_link_key(hdev, key, persistent);
 
        /* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
@@ -3247,15 +3326,16 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
         */
        if (key->type == HCI_LK_DEBUG_COMBINATION &&
            !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
-               list_del(&key->list);
-               kfree(key);
-       } else if (conn) {
-               if (persistent)
-                       clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
-               else
-                       set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+               list_del_rcu(&key->list);
+               kfree_rcu(key, rcu);
+               goto unlock;
        }
 
+       if (persistent)
+               clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+       else
+               set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+
 unlock:
        hci_dev_unlock(hdev);
 }
@@ -3947,16 +4027,14 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
 
        data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
        if (data) {
-               if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+               if (bredr_sc_enabled(hdev)) {
                        struct hci_cp_remote_oob_ext_data_reply cp;
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
                        memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
-                       memcpy(cp.randomizer192, data->randomizer192,
-                              sizeof(cp.randomizer192));
+                       memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
                        memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
-                       memcpy(cp.randomizer256, data->randomizer256,
-                              sizeof(cp.randomizer256));
+                       memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
 
                        hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
                                     sizeof(cp), &cp);
@@ -3965,8 +4043,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
                        memcpy(cp.hash, data->hash192, sizeof(cp.hash));
-                       memcpy(cp.randomizer, data->randomizer192,
-                              sizeof(cp.randomizer));
+                       memcpy(cp.rand, data->rand192, sizeof(cp.rand));
 
                        hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
                                     sizeof(cp), &cp);
@@ -4513,10 +4590,20 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn == NULL)
                goto not_found;
 
-       ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
-       if (ltk == NULL)
+       ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role);
+       if (!ltk)
                goto not_found;
 
+       if (smp_ltk_is_sc(ltk)) {
+               /* With SC both EDiv and Rand are set to zero */
+               if (ev->ediv || ev->rand)
+                       goto not_found;
+       } else {
+               /* For non-SC keys check that EDiv and Rand match */
+               if (ev->ediv != ltk->ediv || ev->rand != ltk->rand)
+                       goto not_found;
+       }
+
        memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
        cp.handle = cpu_to_le16(conn->handle);
 
@@ -4534,8 +4621,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
         */
        if (ltk->type == SMP_STK) {
                set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
-               list_del(&ltk->list);
-               kfree(ltk);
+               list_del_rcu(&ltk->list);
+               kfree_rcu(ltk, rcu);
        } else {
                clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
        }
@@ -4743,6 +4830,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cmd_status_evt(hdev, skb);
                break;
 
+       case HCI_EV_HARDWARE_ERROR:
+               hci_hardware_error_evt(hdev, skb);
+               break;
+
        case HCI_EV_ROLE_CHANGE:
                hci_role_change_evt(hdev, skb);
                break;