diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-09-30 14:52:16 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-09-30 14:52:29 -0400 |
commit | 8e00f5fbb4ecbc3431fa686cba60cd76a62604af (patch) | |
tree | 78aa3bdef892dbd1afc8969fc528e529cfc266f7 /net/bluetooth | |
parent | 56fd49e399ce1d82200fad5b8924d4e35a587809 (diff) | |
parent | 0874073570d9184a169a4ae2abbe8dbcb71878e9 (diff) | |
download | linux-stable-8e00f5fbb4ecbc3431fa686cba60cd76a62604af.tar.gz linux-stable-8e00f5fbb4ecbc3431fa686cba60cd76a62604af.tar.bz2 linux-stable-8e00f5fbb4ecbc3431fa686cba60cd76a62604af.zip |
Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into for-davem
Conflicts:
drivers/net/wireless/iwlwifi/iwl-pci.c
drivers/net/wireless/wl12xx/main.c
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hci_conn.c | 2 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 57 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 16 | ||||
-rw-r--r-- | net/bluetooth/hci_sock.c | 18 | ||||
-rw-r--r-- | net/bluetooth/hci_sysfs.c | 2 | ||||
-rw-r--r-- | net/bluetooth/hidp/core.c | 3 | ||||
-rw-r--r-- | net/bluetooth/l2cap_core.c | 18 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 212 | ||||
-rw-r--r-- | net/bluetooth/smp.c | 421 |
9 files changed, 473 insertions, 276 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ea7f031f3b04..c2df7bf1d374 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -218,7 +218,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], cp.handle = cpu_to_le16(conn->handle); memcpy(cp.ltk, ltk, sizeof(cp.ltk)); cp.ediv = ediv; - memcpy(cp.rand, rand, sizeof(rand)); + memcpy(cp.rand, rand, sizeof(cp.rand)); hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 56943add45cc..b84458dcc226 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1312,59 +1312,41 @@ int hci_blacklist_clear(struct hci_dev *hdev) int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err; if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - hci_dev_lock_bh(hdev); - - if (hci_blacklist_lookup(hdev, bdaddr)) { - err = -EEXIST; - goto err; - } + if (hci_blacklist_lookup(hdev, bdaddr)) + return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto err; - } + if (!entry) + return -ENOMEM; bacpy(&entry->bdaddr, bdaddr); list_add(&entry->list, &hdev->blacklist); - err = 0; - -err: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_blocked(hdev->id, bdaddr); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err = 0; - - hci_dev_lock_bh(hdev); if (bacmp(bdaddr, BDADDR_ANY) == 0) { - hci_blacklist_clear(hdev); - goto done; + return hci_blacklist_clear(hdev); } entry = hci_blacklist_lookup(hdev, bdaddr); if (!entry) { - err = -ENOENT; - goto done; + return -ENOENT; } list_del(&entry->list); kfree(entry); -done: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_unblocked(hdev->id, bdaddr); } static void hci_clear_adv_cache(unsigned long arg) @@ -1523,11 +1505,6 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hdev->tfm)) - BT_INFO("Failed to load transform for ecb(aes): %ld", - PTR_ERR(hdev->tfm)); - hci_register_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, @@ -1576,9 +1553,6 @@ int hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev->id); - if (!IS_ERR(hdev->tfm)) - crypto_free_blkcipher(hdev->tfm); - hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { @@ -2074,6 +2048,9 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int min = c->sent; conn = c; } + + if (hci_conn_num(hdev, type) == num) + break; } if (conn) { @@ -2131,6 +2108,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ACL_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ @@ -2162,6 +2142,9 @@ static inline void hci_sched_sco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, SCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2182,6 +2165,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ESCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2202,6 +2188,9 @@ static inline void hci_sched_le(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, LE_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7ef4eb4435fb..35083f2aa2ea 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -898,16 +898,15 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, if (!cp) return; - hci_dev_lock(hdev); - if (cp->enable == 0x01) { del_timer(&hdev->adv_timer); + + hci_dev_lock(hdev); hci_adv_entries_clear(hdev); + hci_dev_unlock(hdev); } else if (cp->enable == 0x00) { mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); } - - hci_dev_unlock(hdev); } static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1103,9 +1102,10 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, return 0; /* Only request authentication for SSP connections or non-SSP - * devices with sec_level HIGH */ + * devices with sec_level HIGH or if MITM protection is requested */ if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) && - conn->pending_sec_level != BT_SECURITY_HIGH) + conn->pending_sec_level != BT_SECURITY_HIGH && + !(conn->auth_type & 0x01)) return 0; return 1; @@ -1412,7 +1412,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); } else conn->state = BT_CONNECTED; @@ -2816,7 +2816,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff goto unlock; } - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ff02cf5e77cc..f6afe3d76a66 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -183,21 +183,35 @@ static int hci_sock_release(struct socket *sock) static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_add(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_add(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_del(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_del(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } /* Ioctls that require bound socket */ diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index a6c3aa8be1f7..22f1a6c87035 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -23,6 +23,8 @@ static inline char *link_typetostr(int type) return "SCO"; case ESCO_LINK: return "eSCO"; + case LE_LINK: + return "LE"; default: return "UNKNOWN"; } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index fb68f344c34a..b83979c548b2 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -872,6 +872,9 @@ static int hidp_start(struct hid_device *hid) struct hidp_session *session = hid->driver_data; struct hid_report *report; + if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) + return 0; + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. report_list, list) hidp_send_report(session, report); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b3bdb482bbe6..1611b3544bb1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -907,6 +907,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (!conn->hcon->out && conn->hcon->type == LE_LINK) l2cap_le_conn_ready(conn); + if (conn->hcon->out && conn->hcon->type == LE_LINK) + smp_conn_security(conn, conn->hcon->pending_sec_level); + read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -986,8 +989,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) del_timer_sync(&conn->info_timer); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { del_timer(&conn->security_timer); + smp_chan_destroy(conn); + } hcon->l2cap_data = NULL; kfree(conn); @@ -1519,7 +1524,9 @@ struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *m return skb; } -struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u16 control, u16 sdulen) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -4093,6 +4100,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("conn %p", conn); + if (hcon->type == LE_LINK) { + smp_distribute_keys(conn, 0); + del_timer(&conn->security_timer); + } + read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -4105,9 +4117,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->scid == L2CAP_CID_LE_DATA) { if (!status && encrypt) { chan->sec_level = hcon->sec_level; - del_timer(&conn->security_timer); l2cap_chan_ready(sk); - smp_distribute_keys(conn, 0); } bh_unlock_sock(sk); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 53e109eb043e..5a94eec06caa 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -908,7 +908,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) struct hci_dev *hdev; struct mgmt_cp_load_keys *cp; u16 key_count, expected_len; - int i, err; + int i; cp = (void *) data; @@ -918,9 +918,9 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) key_count = get_unaligned_le16(&cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); - if (expected_len > len) { - BT_ERR("load_keys: expected at least %u bytes, got %u bytes", - expected_len, len); + if (expected_len != len) { + BT_ERR("load_keys: expected %u bytes, got %u bytes", + len, expected_len); return -EINVAL; } @@ -942,36 +942,17 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) else clear_bit(HCI_DEBUG_KEYS, &hdev->flags); - len -= sizeof(*cp); - i = 0; - - while (i < len) { - struct mgmt_key_info *key = (void *) cp->keys + i; - - i += sizeof(*key) + key->dlen; - - if (key->type == HCI_LK_SMP_LTK) { - struct key_master_id *id = (void *) key->data; - - if (key->dlen != sizeof(struct key_master_id)) - continue; - - hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len, - id->ediv, id->rand, key->val); - - continue; - } + for (i = 0; i < key_count; i++) { + struct mgmt_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); } - err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0); - hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return err; + return 0; } static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) @@ -1347,6 +1328,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) struct hci_dev *hdev; struct mgmt_cp_pair_device *cp; struct pending_cmd *cmd; + struct adv_entry *entry; u8 sec_level, auth_type; struct hci_conn *conn; int err; @@ -1364,15 +1346,20 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) hci_dev_lock_bh(hdev); - if (cp->io_cap == 0x03) { - sec_level = BT_SECURITY_MEDIUM; + sec_level = BT_SECURITY_MEDIUM; + if (cp->io_cap == 0x03) auth_type = HCI_AT_DEDICATED_BONDING; - } else { - sec_level = BT_SECURITY_HIGH; + else auth_type = HCI_AT_DEDICATED_BONDING_MITM; - } - conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type); + entry = hci_find_adv_entry(hdev, &cp->bdaddr); + if (entry) + conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level, + auth_type); + else + conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, + auth_type); + if (IS_ERR(conn)) { err = PTR_ERR(conn); goto unlock; @@ -1391,7 +1378,10 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) goto unlock; } - conn->connect_cfm_cb = pairing_complete_cb; + /* For LE, just connecting isn't a proof that the pairing finished */ + if (!entry) + conn->connect_cfm_cb = pairing_complete_cb; + conn->security_cfm_cb = pairing_complete_cb; conn->disconn_cfm_cb = pairing_complete_cb; conn->io_capability = cp->io_cap; @@ -1689,13 +1679,12 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_block_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_block_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, EINVAL); @@ -1705,6 +1694,14 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_add(hdev, &cp->bdaddr); if (err < 0) @@ -1712,6 +1709,11 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1721,13 +1723,12 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_unblock_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_unblock_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, EINVAL); @@ -1737,6 +1738,14 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_del(hdev, &cp->bdaddr); if (err < 0) @@ -1744,6 +1753,67 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int set_fast_connectable(struct sock *sk, u16 index, + unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_fast_connectable *cp = (void *) data; + struct hci_cp_write_page_scan_activity acp; + u8 type; + int err; + + BT_DBG("hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + ENODEV); + + hci_dev_lock(hdev); + + if (cp->enable) { + type = PAGE_SCAN_TYPE_INTERLACED; + acp.interval = 0x0024; /* 22.5 msec page scan interval */ + } else { + type = PAGE_SCAN_TYPE_STANDARD; /* default */ + acp.interval = 0x0800; /* default 1.28 sec page scan */ + } + + acp.window = 0x0012; /* default 11.25 msec page scan window */ + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, + sizeof(acp), &acp); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + NULL, 0); +done: + hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1869,6 +1939,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_UNBLOCK_DEVICE: err = unblock_device(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_FAST_CONNECTABLE: + err = set_fast_connectable(sk, index, buf + sizeof(*hdr), + len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); @@ -1977,35 +2051,25 @@ int mgmt_connectable(u16 index, u8 connectable) int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) { - struct mgmt_ev_new_key *ev; - int err, total; - - total = sizeof(struct mgmt_ev_new_key) + key->dlen; - ev = kzalloc(total, GFP_ATOMIC); - if (!ev) - return -ENOMEM; - - bacpy(&ev->key.bdaddr, &key->bdaddr); - ev->key.type = key->type; - memcpy(ev->key.val, key->val, 16); - ev->key.pin_len = key->pin_len; - ev->key.dlen = key->dlen; - ev->store_hint = persistent; + struct mgmt_ev_new_key ev; - memcpy(ev->key.data, key->data, key->dlen); - - err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL); + memset(&ev, 0, sizeof(ev)); - kfree(ev); + ev.store_hint = persistent; + bacpy(&ev.key.bdaddr, &key->bdaddr); + ev.key.type = key->type; + memcpy(ev.key.val, key->val, 16); + ev.key.pin_len = key->pin_len; - return err; + return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); } -int mgmt_connected(u16 index, bdaddr_t *bdaddr) +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) { struct mgmt_ev_connected ev; bacpy(&ev.bdaddr, bdaddr); + ev.link_type = link_type; return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } @@ -2260,12 +2324,14 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, memset(&ev, 0, sizeof(ev)); bacpy(&ev.bdaddr, bdaddr); - memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); ev.rssi = rssi; if (eir) memcpy(ev.eir, eir, sizeof(ev.eir)); + if (dev_class) + memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); + return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL); } @@ -2286,3 +2352,29 @@ int mgmt_discovering(u16 index, u8 discovering) return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, sizeof(discovering), NULL); } + +int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_blocked ev; + + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} + +int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_unblocked ev; + + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 391888b88a92..759b63572641 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -182,18 +182,9 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) return; hci_send_acl(conn->hcon, skb, 0); -} - -static __u8 seclevel_to_authreq(__u8 level) -{ - switch (level) { - case BT_SECURITY_HIGH: - /* Right now we don't support bonding */ - return SMP_AUTH_MITM; - default: - return SMP_AUTH_NONE; - } + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); } static void build_pairing_cmd(struct l2cap_conn *conn, @@ -205,7 +196,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, dist_keys = 0; if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) { - dist_keys = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; + dist_keys = SMP_DIST_ENC_KEY; authreq |= SMP_AUTH_BONDING; } @@ -229,24 +220,184 @@ static void build_pairing_cmd(struct l2cap_conn *conn, static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) { + struct smp_chan *smp = conn->smp_chan; + if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; - conn->smp_key_size = max_key_size; + smp->smp_key_size = max_key_size; return 0; } +static void confirm_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, confirm); + struct l2cap_conn *conn = smp->conn; + struct crypto_blkcipher *tfm; + struct smp_cmd_pairing_confirm cp; + int ret; + u8 res[16], reason; + + BT_DBG("conn %p", conn); + + tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + smp->tfm = tfm; + + if (conn->hcon->out) + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, cp.confirm_val); + smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static void random_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, random); + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct crypto_blkcipher *tfm = smp->tfm; + u8 reason, confirm[16], res[16], key[16]; + int ret; + + if (IS_ERR_OR_NULL(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + if (hcon->out) + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, + conn->src, hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, + hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, confirm); + + if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { + BT_ERR("Pairing failed (confirmation values mismatch)"); + reason = SMP_CONFIRM_FAILED; + goto error; + } + + if (hcon->out) { + u8 stk[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + hci_le_start_enc(hcon, ediv, rand, stk); + hcon->enc_key_size = smp->smp_key_size; + } else { + u8 stk[16], r[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + swap128(smp->prnd, r); + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + + smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size, + ediv, rand, stk); + } + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) +{ + struct smp_chan *smp; + + smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); + if (!smp) + return NULL; + + INIT_WORK(&smp->confirm, confirm_work); + INIT_WORK(&smp->random, random_work); + + smp->conn = conn; + conn->smp_chan = smp; + + hci_conn_hold(conn->hcon); + + return smp; +} + +void smp_chan_destroy(struct l2cap_conn *conn) +{ + kfree(conn->smp_chan); + hci_conn_put(conn->hcon); +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; + struct smp_chan *smp; u8 key_size; + int ret; BT_DBG("conn %p", conn); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], req, sizeof(*req)); + if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + smp = smp_chan_create(conn); + + smp = conn->smp_chan; + + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); if (req->oob_flag) @@ -260,32 +411,33 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_ENC_KEY_SIZE; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); + memset(smp->tk, 0, sizeof(smp->tk)); + + ret = smp_rand(smp->prnd); + if (ret) + return SMP_UNSPECIFIED; - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - return 0; } static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_cmd_pairing_confirm cp; - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; + u8 key_size; int ret; - u8 res[16], key_size; BT_DBG("conn %p", conn); skb_pull(skb, sizeof(*rsp)); - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; key_size = min(req->max_key_size, rsp->max_key_size); if (check_enc_key_size(conn, key_size)) @@ -295,222 +447,154 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_OOB_NOT_AVAIL; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); - - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rsp, sizeof(*rsp)); - - ret = smp_rand(conn->prnd); - if (ret) - return SMP_UNSPECIFIED; + memset(smp->tk, 0, sizeof(smp->tk)); - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); + ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; - swap128(res, cp.confirm_val); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); return 0; } static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf)); - skb_pull(skb, sizeof(conn->pcnf)); + memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); + skb_pull(skb, sizeof(smp->pcnf)); if (conn->hcon->out) { u8 random[16]; - swap128(conn->prnd, random); + swap128(smp->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), random); } else { - struct smp_cmd_pairing_confirm cp; - int ret; - u8 res[16]; - - ret = smp_rand(conn->prnd); - if (ret) - return SMP_UNSPECIFIED; - - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, - conn->hcon->dst_type, conn->dst, - 0, conn->src, res); - if (ret) - return SMP_CONFIRM_FAILED; - - swap128(res, cp.confirm_val); - - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); } - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - return 0; } static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct hci_conn *hcon = conn->hcon; - struct crypto_blkcipher *tfm = hcon->hdev->tfm; - int ret; - u8 key[16], res[16], random[16], confirm[16]; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; - swap128(skb->data, random); - skb_pull(skb, sizeof(random)); - - if (conn->hcon->out) - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, - res); - else - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); - if (ret) - return SMP_UNSPECIFIED; - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - swap128(res, confirm); - - if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { - BT_ERR("Pairing failed (confirmation values mismatch)"); - return SMP_CONFIRM_FAILED; - } - - if (conn->hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; + BT_DBG("conn %p", conn); - smp_s1(tfm, conn->tk, random, conn->prnd, key); - swap128(key, stk); + swap128(skb->data, smp->rrnd); + skb_pull(skb, sizeof(smp->rrnd)); - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + queue_work(hdev->workqueue, &smp->random); - hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = conn->smp_key_size; - } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; + return 0; +} - memset(rand, 0, sizeof(rand)); - ediv = 0; +static u8 smp_ltk_encrypt(struct l2cap_conn *conn) +{ + struct link_key *key; + struct key_master_id *master; + struct hci_conn *hcon = conn->hcon; - swap128(conn->prnd, r); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + key = hci_find_link_key_type(hcon->hdev, conn->dst, + HCI_LK_SMP_LTK); + if (!key) + return 0; - smp_s1(tfm, conn->tk, conn->prnd, random, key); - swap128(key, stk); + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, + &hcon->pend)) + return 1; - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + master = (void *) key->data; + hci_le_start_enc(hcon, master->ediv, master->rand, + key->val); + hcon->enc_key_size = key->pin_len; - hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size, - ediv, rand, stk); - } + return 1; - return 0; } - static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; + struct smp_chan *smp; BT_DBG("conn %p", conn); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + hcon->pending_sec_level = BT_SECURITY_MEDIUM; + + if (smp_ltk_encrypt(conn)) return 0; + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + return 0; + + smp = smp_chan_create(conn); + skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); - return 0; } int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { struct hci_conn *hcon = conn->hcon; - __u8 authreq; + struct smp_chan *smp = conn->smp_chan; BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); if (!lmp_host_le_capable(hcon->hdev)) return 1; - if (IS_ERR(hcon->hdev->tfm)) - return 1; - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) - return 0; - if (sec_level == BT_SECURITY_LOW) return 1; if (hcon->sec_level >= sec_level) return 1; - authreq = seclevel_to_authreq(sec_level); - - if (hcon->link_mode & HCI_LM_MASTER) { - struct smp_cmd_pairing cp; - struct link_key *key; + if (hcon->link_mode & HCI_LM_MASTER) + if (smp_ltk_encrypt(conn)) + goto done; - key = hci_find_link_key_type(hcon->hdev, conn->dst, - HCI_LK_SMP_LTK); - if (key) { - struct key_master_id *master = (void *) key->data; + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + return 0; - hci_le_start_enc(hcon, master->ediv, master->rand, - key->val); - hcon->enc_key_size = key->pin_len; + smp = smp_chan_create(conn); - goto done; - } - - build_pairing_cmd(conn, &cp, NULL, authreq); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + if (hcon->link_mode & HCI_LM_MASTER) { + struct smp_cmd_pairing cp; - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); + build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; - cp.auth_req = authreq; + cp.auth_req = SMP_AUTH_NONE; smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); } done: hcon->pending_sec_level = sec_level; - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; } @@ -518,10 +602,11 @@ done: static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_encrypt_info *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - memcpy(conn->tk, rp->ltk, sizeof(conn->tk)); + memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); return 0; } @@ -529,11 +614,12 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size, - rp->ediv, rp->rand, conn->tk); + hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size, + rp->ediv, rp->rand, smp->tk); smp_distribute_keys(conn, 1); @@ -552,12 +638,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) goto done; } - if (IS_ERR(conn->hcon->hdev->tfm)) { - err = PTR_ERR(conn->hcon->hdev->tfm); - reason = SMP_PAIRING_NOTSUPP; - goto done; - } - skb_pull(skb, sizeof(code)); switch (code) { @@ -621,20 +701,21 @@ done: int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) { struct smp_cmd_pairing *req, *rsp; + struct smp_chan *smp = conn->smp_chan; __u8 *keydist; BT_DBG("conn %p force %d", conn, force); - if (IS_ERR(conn->hcon->hdev->tfm)) - return PTR_ERR(conn->hcon->hdev->tfm); + if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + return 0; - rsp = (void *) &conn->prsp[1]; + rsp = (void *) &smp->prsp[1]; /* The responder sends its keys first */ if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) return 0; - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; if (conn->hcon->out) { keydist = &rsp->init_key_dist; @@ -658,7 +739,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size, ediv, ident.rand, enc.ltk); ident.ediv = cpu_to_le16(ediv); @@ -698,5 +779,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) *keydist &= ~SMP_DIST_SIGN; } + if (conn->hcon->out || force) { + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + del_timer(&conn->security_timer); + smp_chan_destroy(conn); + } + return 0; } |