Merge branch 'master' of git://github.com/padovan/bluetooth-next
This commit is contained in:
commit
bb8f2cb284
|
@ -716,6 +716,16 @@ struct hci_rp_read_bd_addr {
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c
|
||||||
|
struct hci_cp_write_page_scan_activity {
|
||||||
|
__le16 interval;
|
||||||
|
__le16 window;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47
|
||||||
|
#define PAGE_SCAN_TYPE_STANDARD 0x00
|
||||||
|
#define PAGE_SCAN_TYPE_INTERLACED 0x01
|
||||||
|
|
||||||
#define HCI_OP_LE_SET_EVENT_MASK 0x2001
|
#define HCI_OP_LE_SET_EVENT_MASK 0x2001
|
||||||
struct hci_cp_le_set_event_mask {
|
struct hci_cp_le_set_event_mask {
|
||||||
__u8 mask[8];
|
__u8 mask[8];
|
||||||
|
|
|
@ -195,8 +195,6 @@ struct hci_dev {
|
||||||
|
|
||||||
__u16 init_last_cmd;
|
__u16 init_last_cmd;
|
||||||
|
|
||||||
struct crypto_blkcipher *tfm;
|
|
||||||
|
|
||||||
struct inquiry_cache inq_cache;
|
struct inquiry_cache inq_cache;
|
||||||
struct hci_conn_hash conn_hash;
|
struct hci_conn_hash conn_hash;
|
||||||
struct list_head blacklist;
|
struct list_head blacklist;
|
||||||
|
@ -348,6 +346,7 @@ enum {
|
||||||
HCI_CONN_RSWITCH_PEND,
|
HCI_CONN_RSWITCH_PEND,
|
||||||
HCI_CONN_MODE_CHANGE_PEND,
|
HCI_CONN_MODE_CHANGE_PEND,
|
||||||
HCI_CONN_SCO_SETUP_PEND,
|
HCI_CONN_SCO_SETUP_PEND,
|
||||||
|
HCI_CONN_LE_SMP_PEND,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void hci_conn_hash_init(struct hci_dev *hdev)
|
static inline void hci_conn_hash_init(struct hci_dev *hdev)
|
||||||
|
@ -395,6 +394,22 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
|
||||||
|
{
|
||||||
|
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||||
|
switch (type) {
|
||||||
|
case ACL_LINK:
|
||||||
|
return h->acl_num;
|
||||||
|
case LE_LINK:
|
||||||
|
return h->le_num;
|
||||||
|
case SCO_LINK:
|
||||||
|
case ESCO_LINK:
|
||||||
|
return h->sco_num;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
|
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
|
||||||
__u16 handle)
|
__u16 handle)
|
||||||
{
|
{
|
||||||
|
@ -475,7 +490,7 @@ static inline void hci_conn_put(struct hci_conn *conn)
|
||||||
{
|
{
|
||||||
if (atomic_dec_and_test(&conn->refcnt)) {
|
if (atomic_dec_and_test(&conn->refcnt)) {
|
||||||
unsigned long timeo;
|
unsigned long timeo;
|
||||||
if (conn->type == ACL_LINK) {
|
if (conn->type == ACL_LINK || conn->type == LE_LINK) {
|
||||||
del_timer(&conn->idle_timer);
|
del_timer(&conn->idle_timer);
|
||||||
if (conn->state == BT_CONNECTED) {
|
if (conn->state == BT_CONNECTED) {
|
||||||
timeo = msecs_to_jiffies(conn->disc_timeout);
|
timeo = msecs_to_jiffies(conn->disc_timeout);
|
||||||
|
@ -838,7 +853,7 @@ int mgmt_powered(u16 index, u8 powered);
|
||||||
int mgmt_discoverable(u16 index, u8 discoverable);
|
int mgmt_discoverable(u16 index, u8 discoverable);
|
||||||
int mgmt_connectable(u16 index, u8 connectable);
|
int mgmt_connectable(u16 index, u8 connectable);
|
||||||
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent);
|
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent);
|
||||||
int mgmt_connected(u16 index, bdaddr_t *bdaddr);
|
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type);
|
||||||
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
|
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr);
|
||||||
int mgmt_disconnect_failed(u16 index);
|
int mgmt_disconnect_failed(u16 index);
|
||||||
int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
|
int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
|
||||||
|
@ -858,6 +873,8 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi,
|
||||||
u8 *eir);
|
u8 *eir);
|
||||||
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
|
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name);
|
||||||
int mgmt_discovering(u16 index, u8 discovering);
|
int mgmt_discovering(u16 index, u8 discovering);
|
||||||
|
int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr);
|
||||||
|
int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr);
|
||||||
|
|
||||||
/* HCI info for socket */
|
/* HCI info for socket */
|
||||||
#define hci_pi(sk) ((struct hci_pinfo *) sk)
|
#define hci_pi(sk) ((struct hci_pinfo *) sk)
|
||||||
|
|
|
@ -409,14 +409,8 @@ struct l2cap_conn {
|
||||||
|
|
||||||
__u8 disc_reason;
|
__u8 disc_reason;
|
||||||
|
|
||||||
__u8 preq[7]; /* SMP Pairing Request */
|
|
||||||
__u8 prsp[7]; /* SMP Pairing Response */
|
|
||||||
__u8 prnd[16]; /* SMP Pairing Random */
|
|
||||||
__u8 pcnf[16]; /* SMP Pairing Confirm */
|
|
||||||
__u8 tk[16]; /* SMP Temporary Key */
|
|
||||||
__u8 smp_key_size;
|
|
||||||
|
|
||||||
struct timer_list security_timer;
|
struct timer_list security_timer;
|
||||||
|
struct smp_chan *smp_chan;
|
||||||
|
|
||||||
struct list_head chan_l;
|
struct list_head chan_l;
|
||||||
rwlock_t chan_lock;
|
rwlock_t chan_lock;
|
||||||
|
|
|
@ -211,6 +211,11 @@ struct mgmt_cp_unblock_device {
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define MGMT_OP_SET_FAST_CONNECTABLE 0x001F
|
||||||
|
struct mgmt_cp_set_fast_connectable {
|
||||||
|
__u8 enable;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||||
struct mgmt_ev_cmd_complete {
|
struct mgmt_ev_cmd_complete {
|
||||||
__le16 opcode;
|
__le16 opcode;
|
||||||
|
@ -249,6 +254,7 @@ struct mgmt_ev_new_key {
|
||||||
#define MGMT_EV_CONNECTED 0x000B
|
#define MGMT_EV_CONNECTED 0x000B
|
||||||
struct mgmt_ev_connected {
|
struct mgmt_ev_connected {
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
|
__u8 link_type;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define MGMT_EV_DISCONNECTED 0x000C
|
#define MGMT_EV_DISCONNECTED 0x000C
|
||||||
|
@ -301,3 +307,13 @@ struct mgmt_ev_remote_name {
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define MGMT_EV_DISCOVERING 0x0014
|
#define MGMT_EV_DISCOVERING 0x0014
|
||||||
|
|
||||||
|
#define MGMT_EV_DEVICE_BLOCKED 0x0015
|
||||||
|
struct mgmt_ev_device_blocked {
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define MGMT_EV_DEVICE_UNBLOCKED 0x0016
|
||||||
|
struct mgmt_ev_device_unblocked {
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
} __packed;
|
||||||
|
|
|
@ -115,9 +115,26 @@ struct smp_cmd_security_req {
|
||||||
#define SMP_MIN_ENC_KEY_SIZE 7
|
#define SMP_MIN_ENC_KEY_SIZE 7
|
||||||
#define SMP_MAX_ENC_KEY_SIZE 16
|
#define SMP_MAX_ENC_KEY_SIZE 16
|
||||||
|
|
||||||
|
struct smp_chan {
|
||||||
|
struct l2cap_conn *conn;
|
||||||
|
u8 preq[7]; /* SMP Pairing Request */
|
||||||
|
u8 prsp[7]; /* SMP Pairing Response */
|
||||||
|
u8 prnd[16]; /* SMP Pairing Random (local) */
|
||||||
|
u8 rrnd[16]; /* SMP Pairing Random (remote) */
|
||||||
|
u8 pcnf[16]; /* SMP Pairing Confirm */
|
||||||
|
u8 tk[16]; /* SMP Temporary Key */
|
||||||
|
u8 smp_key_size;
|
||||||
|
struct crypto_blkcipher *tfm;
|
||||||
|
struct work_struct confirm;
|
||||||
|
struct work_struct random;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/* SMP Commands */
|
/* SMP Commands */
|
||||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
|
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
|
||||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
||||||
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
|
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
|
||||||
|
|
||||||
|
void smp_chan_destroy(struct l2cap_conn *conn);
|
||||||
|
|
||||||
#endif /* __SMP_H */
|
#endif /* __SMP_H */
|
||||||
|
|
|
@ -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);
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||||
cp.ediv = ediv;
|
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);
|
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1312,59 +1312,41 @@ int hci_blacklist_clear(struct hci_dev *hdev)
|
||||||
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||||
{
|
{
|
||||||
struct bdaddr_list *entry;
|
struct bdaddr_list *entry;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
|
|
||||||
hci_dev_lock_bh(hdev);
|
if (hci_blacklist_lookup(hdev, bdaddr))
|
||||||
|
return -EEXIST;
|
||||||
if (hci_blacklist_lookup(hdev, bdaddr)) {
|
|
||||||
err = -EEXIST;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||||
if (!entry) {
|
if (!entry)
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
bacpy(&entry->bdaddr, bdaddr);
|
bacpy(&entry->bdaddr, bdaddr);
|
||||||
|
|
||||||
list_add(&entry->list, &hdev->blacklist);
|
list_add(&entry->list, &hdev->blacklist);
|
||||||
|
|
||||||
err = 0;
|
return mgmt_device_blocked(hdev->id, bdaddr);
|
||||||
|
|
||||||
err:
|
|
||||||
hci_dev_unlock_bh(hdev);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||||
{
|
{
|
||||||
struct bdaddr_list *entry;
|
struct bdaddr_list *entry;
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
hci_dev_lock_bh(hdev);
|
|
||||||
|
|
||||||
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
||||||
hci_blacklist_clear(hdev);
|
return hci_blacklist_clear(hdev);
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = hci_blacklist_lookup(hdev, bdaddr);
|
entry = hci_blacklist_lookup(hdev, bdaddr);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
err = -ENOENT;
|
return -ENOENT;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list_del(&entry->list);
|
list_del(&entry->list);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
|
|
||||||
done:
|
return mgmt_device_unblocked(hdev->id, bdaddr);
|
||||||
hci_dev_unlock_bh(hdev);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_clear_adv_cache(unsigned long arg)
|
static void hci_clear_adv_cache(unsigned long arg)
|
||||||
|
@ -1523,11 +1505,6 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||||
if (!hdev->workqueue)
|
if (!hdev->workqueue)
|
||||||
goto nomem;
|
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);
|
hci_register_sysfs(hdev);
|
||||||
|
|
||||||
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
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))
|
!test_bit(HCI_SETUP, &hdev->flags))
|
||||||
mgmt_index_removed(hdev->id);
|
mgmt_index_removed(hdev->id);
|
||||||
|
|
||||||
if (!IS_ERR(hdev->tfm))
|
|
||||||
crypto_free_blkcipher(hdev->tfm);
|
|
||||||
|
|
||||||
hci_notify(hdev, HCI_DEV_UNREG);
|
hci_notify(hdev, HCI_DEV_UNREG);
|
||||||
|
|
||||||
if (hdev->rfkill) {
|
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;
|
min = c->sent;
|
||||||
conn = c;
|
conn = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hci_conn_num(hdev, type) == num)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn) {
|
if (conn) {
|
||||||
|
@ -2131,6 +2108,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (!hci_conn_num(hdev, ACL_LINK))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
||||||
/* ACL tx timeout must be longer than maximum
|
/* ACL tx timeout must be longer than maximum
|
||||||
* link supervision timeout (40.9 seconds) */
|
* 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);
|
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 (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) {
|
||||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||||
BT_DBG("skb %p len %d", skb, skb->len);
|
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);
|
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 (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) {
|
||||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||||
BT_DBG("skb %p len %d", skb, skb->len);
|
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);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (!hci_conn_num(hdev, LE_LINK))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
||||||
/* LE tx timeout must be longer than maximum
|
/* LE tx timeout must be longer than maximum
|
||||||
* link supervision timeout (40.9 seconds) */
|
* link supervision timeout (40.9 seconds) */
|
||||||
|
|
|
@ -898,16 +898,15 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||||
if (!cp)
|
if (!cp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
|
|
||||||
if (cp->enable == 0x01) {
|
if (cp->enable == 0x01) {
|
||||||
del_timer(&hdev->adv_timer);
|
del_timer(&hdev->adv_timer);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
hci_adv_entries_clear(hdev);
|
hci_adv_entries_clear(hdev);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
} else if (cp->enable == 0x00) {
|
} else if (cp->enable == 0x00) {
|
||||||
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
|
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)
|
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;
|
return 0;
|
||||||
|
|
||||||
/* Only request authentication for SSP connections or non-SSP
|
/* 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) &&
|
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 0;
|
||||||
|
|
||||||
return 1;
|
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;
|
conn->state = BT_CONFIG;
|
||||||
hci_conn_hold(conn);
|
hci_conn_hold(conn);
|
||||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||||
mgmt_connected(hdev->id, &ev->bdaddr);
|
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
|
||||||
} else
|
} else
|
||||||
conn->state = BT_CONNECTED;
|
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;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmt_connected(hdev->id, &ev->bdaddr);
|
mgmt_connected(hdev->id, &ev->bdaddr, conn->type);
|
||||||
|
|
||||||
conn->sec_level = BT_SECURITY_LOW;
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
conn->handle = __le16_to_cpu(ev->handle);
|
conn->handle = __le16_to_cpu(ev->handle);
|
||||||
|
|
|
@ -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)
|
static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||||
{
|
{
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||||
return -EFAULT;
|
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)
|
static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||||
{
|
{
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||||
return -EFAULT;
|
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 */
|
/* Ioctls that require bound socket */
|
||||||
|
|
|
@ -23,6 +23,8 @@ static inline char *link_typetostr(int type)
|
||||||
return "SCO";
|
return "SCO";
|
||||||
case ESCO_LINK:
|
case ESCO_LINK:
|
||||||
return "eSCO";
|
return "eSCO";
|
||||||
|
case LE_LINK:
|
||||||
|
return "LE";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,6 +872,9 @@ static int hidp_start(struct hid_device *hid)
|
||||||
struct hidp_session *session = hid->driver_data;
|
struct hidp_session *session = hid->driver_data;
|
||||||
struct hid_report *report;
|
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].
|
list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
|
||||||
report_list, list)
|
report_list, list)
|
||||||
hidp_send_report(session, report);
|
hidp_send_report(session, report);
|
||||||
|
|
|
@ -907,6 +907,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
||||||
if (!conn->hcon->out && conn->hcon->type == LE_LINK)
|
if (!conn->hcon->out && conn->hcon->type == LE_LINK)
|
||||||
l2cap_le_conn_ready(conn);
|
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);
|
read_lock(&conn->chan_lock);
|
||||||
|
|
||||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
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)
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
||||||
del_timer_sync(&conn->info_timer);
|
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);
|
del_timer(&conn->security_timer);
|
||||||
|
smp_chan_destroy(conn);
|
||||||
|
}
|
||||||
|
|
||||||
hcon->l2cap_data = NULL;
|
hcon->l2cap_data = NULL;
|
||||||
kfree(conn);
|
kfree(conn);
|
||||||
|
@ -1519,7 +1524,9 @@ struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *m
|
||||||
return skb;
|
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 sock *sk = chan->sk;
|
||||||
struct l2cap_conn *conn = chan->conn;
|
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);
|
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);
|
read_lock(&conn->chan_lock);
|
||||||
|
|
||||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
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 (chan->scid == L2CAP_CID_LE_DATA) {
|
||||||
if (!status && encrypt) {
|
if (!status && encrypt) {
|
||||||
chan->sec_level = hcon->sec_level;
|
chan->sec_level = hcon->sec_level;
|
||||||
del_timer(&conn->security_timer);
|
|
||||||
l2cap_chan_ready(sk);
|
l2cap_chan_ready(sk);
|
||||||
smp_distribute_keys(conn, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bh_unlock_sock(sk);
|
bh_unlock_sock(sk);
|
||||||
|
|
|
@ -908,7 +908,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
struct mgmt_cp_load_keys *cp;
|
struct mgmt_cp_load_keys *cp;
|
||||||
u16 key_count, expected_len;
|
u16 key_count, expected_len;
|
||||||
int i, err;
|
int i;
|
||||||
|
|
||||||
cp = (void *) data;
|
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);
|
key_count = get_unaligned_le16(&cp->key_count);
|
||||||
|
|
||||||
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
|
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
|
||||||
if (expected_len > len) {
|
if (expected_len != len) {
|
||||||
BT_ERR("load_keys: expected at least %u bytes, got %u bytes",
|
BT_ERR("load_keys: expected %u bytes, got %u bytes",
|
||||||
expected_len, len);
|
len, expected_len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,36 +942,17 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||||
else
|
else
|
||||||
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
|
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
|
||||||
|
|
||||||
len -= sizeof(*cp);
|
for (i = 0; i < key_count; i++) {
|
||||||
i = 0;
|
struct mgmt_key_info *key = &cp->keys[i];
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
|
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
|
||||||
key->pin_len);
|
key->pin_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0);
|
|
||||||
|
|
||||||
hci_dev_unlock_bh(hdev);
|
hci_dev_unlock_bh(hdev);
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
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 hci_dev *hdev;
|
||||||
struct mgmt_cp_pair_device *cp;
|
struct mgmt_cp_pair_device *cp;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
|
struct adv_entry *entry;
|
||||||
u8 sec_level, auth_type;
|
u8 sec_level, auth_type;
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
int err;
|
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);
|
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;
|
auth_type = HCI_AT_DEDICATED_BONDING;
|
||||||
} else {
|
else
|
||||||
sec_level = BT_SECURITY_HIGH;
|
|
||||||
auth_type = HCI_AT_DEDICATED_BONDING_MITM;
|
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)) {
|
if (IS_ERR(conn)) {
|
||||||
err = PTR_ERR(conn);
|
err = PTR_ERR(conn);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
@ -1391,7 +1378,10 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||||
goto unlock;
|
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->security_cfm_cb = pairing_complete_cb;
|
||||||
conn->disconn_cfm_cb = pairing_complete_cb;
|
conn->disconn_cfm_cb = pairing_complete_cb;
|
||||||
conn->io_capability = cp->io_cap;
|
conn->io_capability = cp->io_cap;
|
||||||
|
@ -1689,13 +1679,12 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
u16 len)
|
u16 len)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
struct mgmt_cp_block_device *cp;
|
struct pending_cmd *cmd;
|
||||||
|
struct mgmt_cp_block_device *cp = (void *) data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("hci%u", index);
|
BT_DBG("hci%u", index);
|
||||||
|
|
||||||
cp = (void *) data;
|
|
||||||
|
|
||||||
if (len != sizeof(*cp))
|
if (len != sizeof(*cp))
|
||||||
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
EINVAL);
|
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,
|
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
ENODEV);
|
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);
|
err = hci_blacklist_add(hdev, &cp->bdaddr);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -1712,6 +1709,11 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
else
|
else
|
||||||
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
|
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
|
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
failed:
|
||||||
|
hci_dev_unlock_bh(hdev);
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -1721,13 +1723,12 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
u16 len)
|
u16 len)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
struct mgmt_cp_unblock_device *cp;
|
struct pending_cmd *cmd;
|
||||||
|
struct mgmt_cp_unblock_device *cp = (void *) data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("hci%u", index);
|
BT_DBG("hci%u", index);
|
||||||
|
|
||||||
cp = (void *) data;
|
|
||||||
|
|
||||||
if (len != sizeof(*cp))
|
if (len != sizeof(*cp))
|
||||||
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
EINVAL);
|
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,
|
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
ENODEV);
|
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);
|
err = hci_blacklist_del(hdev, &cp->bdaddr);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -1744,6 +1753,67 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
else
|
else
|
||||||
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
NULL, 0);
|
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);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -1869,6 +1939,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||||
case MGMT_OP_UNBLOCK_DEVICE:
|
case MGMT_OP_UNBLOCK_DEVICE:
|
||||||
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
|
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
|
||||||
break;
|
break;
|
||||||
|
case MGMT_OP_SET_FAST_CONNECTABLE:
|
||||||
|
err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
|
||||||
|
len);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BT_DBG("Unknown op %u", opcode);
|
BT_DBG("Unknown op %u", opcode);
|
||||||
err = cmd_status(sk, index, opcode, 0x01);
|
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)
|
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
|
||||||
{
|
{
|
||||||
struct mgmt_ev_new_key *ev;
|
struct mgmt_ev_new_key ev;
|
||||||
int err, total;
|
|
||||||
|
|
||||||
total = sizeof(struct mgmt_ev_new_key) + key->dlen;
|
memset(&ev, 0, sizeof(ev));
|
||||||
ev = kzalloc(total, GFP_ATOMIC);
|
|
||||||
if (!ev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
bacpy(&ev->key.bdaddr, &key->bdaddr);
|
ev.store_hint = persistent;
|
||||||
ev->key.type = key->type;
|
bacpy(&ev.key.bdaddr, &key->bdaddr);
|
||||||
memcpy(ev->key.val, key->val, 16);
|
ev.key.type = key->type;
|
||||||
ev->key.pin_len = key->pin_len;
|
memcpy(ev.key.val, key->val, 16);
|
||||||
ev->key.dlen = key->dlen;
|
ev.key.pin_len = key->pin_len;
|
||||||
ev->store_hint = persistent;
|
|
||||||
|
|
||||||
memcpy(ev->key.data, key->data, key->dlen);
|
return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
|
||||||
|
|
||||||
err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
|
|
||||||
|
|
||||||
kfree(ev);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mgmt_connected(u16 index, bdaddr_t *bdaddr)
|
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type)
|
||||||
{
|
{
|
||||||
struct mgmt_ev_connected ev;
|
struct mgmt_ev_connected ev;
|
||||||
|
|
||||||
bacpy(&ev.bdaddr, bdaddr);
|
bacpy(&ev.bdaddr, bdaddr);
|
||||||
|
ev.link_type = link_type;
|
||||||
|
|
||||||
return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL);
|
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));
|
memset(&ev, 0, sizeof(ev));
|
||||||
|
|
||||||
bacpy(&ev.bdaddr, bdaddr);
|
bacpy(&ev.bdaddr, bdaddr);
|
||||||
memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
|
|
||||||
ev.rssi = rssi;
|
ev.rssi = rssi;
|
||||||
|
|
||||||
if (eir)
|
if (eir)
|
||||||
memcpy(ev.eir, eir, sizeof(ev.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);
|
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,
|
return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering,
|
||||||
sizeof(discovering), NULL);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -182,18 +182,9 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_send_acl(conn->hcon, skb, 0);
|
hci_send_acl(conn->hcon, skb, 0);
|
||||||
}
|
|
||||||
|
|
||||||
static __u8 seclevel_to_authreq(__u8 level)
|
mod_timer(&conn->security_timer, jiffies +
|
||||||
{
|
msecs_to_jiffies(SMP_TIMEOUT));
|
||||||
switch (level) {
|
|
||||||
case BT_SECURITY_HIGH:
|
|
||||||
/* Right now we don't support bonding */
|
|
||||||
return SMP_AUTH_MITM;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return SMP_AUTH_NONE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
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;
|
dist_keys = 0;
|
||||||
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
|
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;
|
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)
|
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) ||
|
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||||
return SMP_ENC_KEY_SIZE;
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
|
||||||
conn->smp_key_size = max_key_size;
|
smp->smp_key_size = max_key_size;
|
||||||
|
|
||||||
return 0;
|
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)
|
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_cmd_pairing rsp, *req = (void *) skb->data;
|
||||||
|
struct smp_chan *smp;
|
||||||
u8 key_size;
|
u8 key_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
|
||||||
memcpy(&conn->preq[1], req, sizeof(*req));
|
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));
|
skb_pull(skb, sizeof(*req));
|
||||||
|
|
||||||
if (req->oob_flag)
|
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;
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
|
||||||
/* Just works */
|
/* Just works */
|
||||||
memset(conn->tk, 0, sizeof(conn->tk));
|
memset(smp->tk, 0, sizeof(smp->tk));
|
||||||
|
|
||||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
ret = smp_rand(smp->prnd);
|
||||||
memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
|
if (ret)
|
||||||
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
|
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);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
|
||||||
|
|
||||||
mod_timer(&conn->security_timer, jiffies +
|
|
||||||
msecs_to_jiffies(SMP_TIMEOUT));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
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 *req, *rsp = (void *) skb->data;
|
||||||
struct smp_cmd_pairing_confirm cp;
|
struct smp_chan *smp = conn->smp_chan;
|
||||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
struct hci_dev *hdev = conn->hcon->hdev;
|
||||||
|
u8 key_size;
|
||||||
int ret;
|
int ret;
|
||||||
u8 res[16], key_size;
|
|
||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*rsp));
|
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);
|
key_size = min(req->max_key_size, rsp->max_key_size);
|
||||||
if (check_enc_key_size(conn, 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;
|
return SMP_OOB_NOT_AVAIL;
|
||||||
|
|
||||||
/* Just works */
|
/* Just works */
|
||||||
memset(conn->tk, 0, sizeof(conn->tk));
|
memset(smp->tk, 0, sizeof(smp->tk));
|
||||||
|
|
||||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
ret = smp_rand(smp->prnd);
|
||||||
memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
|
|
||||||
|
|
||||||
ret = smp_rand(conn->prnd);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return SMP_UNSPECIFIED;
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||||
conn->src, conn->hcon->dst_type, conn->dst, res);
|
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
|
||||||
if (ret)
|
|
||||||
return SMP_UNSPECIFIED;
|
|
||||||
|
|
||||||
swap128(res, cp.confirm_val);
|
queue_work(hdev->workqueue, &smp->confirm);
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
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");
|
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||||
|
|
||||||
memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
|
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
|
||||||
skb_pull(skb, sizeof(conn->pcnf));
|
skb_pull(skb, sizeof(smp->pcnf));
|
||||||
|
|
||||||
if (conn->hcon->out) {
|
if (conn->hcon->out) {
|
||||||
u8 random[16];
|
u8 random[16];
|
||||||
|
|
||||||
swap128(conn->prnd, random);
|
swap128(smp->prnd, random);
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
||||||
random);
|
random);
|
||||||
} else {
|
} else {
|
||||||
struct smp_cmd_pairing_confirm cp;
|
queue_work(hdev->workqueue, &smp->confirm);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod_timer(&conn->security_timer, jiffies +
|
|
||||||
msecs_to_jiffies(SMP_TIMEOUT));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_conn *hcon = conn->hcon;
|
struct smp_chan *smp = conn->smp_chan;
|
||||||
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
struct hci_dev *hdev = conn->hcon->hdev;
|
||||||
int ret;
|
|
||||||
u8 key[16], res[16], random[16], confirm[16];
|
|
||||||
|
|
||||||
swap128(skb->data, random);
|
BT_DBG("conn %p", conn);
|
||||||
skb_pull(skb, sizeof(random));
|
|
||||||
|
|
||||||
if (conn->hcon->out)
|
swap128(skb->data, smp->rrnd);
|
||||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
|
skb_pull(skb, sizeof(smp->rrnd));
|
||||||
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");
|
queue_work(hdev->workqueue, &smp->random);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
|
||||||
swap128(key, stk);
|
|
||||||
|
|
||||||
memset(stk + conn->smp_key_size, 0,
|
|
||||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
memset(rand, 0, sizeof(rand));
|
|
||||||
ediv = 0;
|
|
||||||
|
|
||||||
swap128(conn->prnd, r);
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
|
|
||||||
|
|
||||||
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
|
||||||
swap128(key, stk);
|
|
||||||
|
|
||||||
memset(stk + conn->smp_key_size, 0,
|
|
||||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
|
||||||
|
|
||||||
hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size,
|
|
||||||
ediv, rand, stk);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 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;
|
||||||
|
|
||||||
|
key = hci_find_link_key_type(hcon->hdev, conn->dst,
|
||||||
|
HCI_LK_SMP_LTK);
|
||||||
|
if (!key)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
|
||||||
|
&hcon->pend))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
master = (void *) key->data;
|
||||||
|
hci_le_start_enc(hcon, master->ediv, master->rand,
|
||||||
|
key->val);
|
||||||
|
hcon->enc_key_size = key->pin_len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
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_security_req *rp = (void *) skb->data;
|
||||||
struct smp_cmd_pairing cp;
|
struct smp_cmd_pairing cp;
|
||||||
struct hci_conn *hcon = conn->hcon;
|
struct hci_conn *hcon = conn->hcon;
|
||||||
|
struct smp_chan *smp;
|
||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
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;
|
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));
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
build_pairing_cmd(conn, &cp, NULL, rp->auth_req);
|
build_pairing_cmd(conn, &cp, NULL, rp->auth_req);
|
||||||
|
|
||||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
smp->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
memcpy(&smp->preq[1], &cp, sizeof(cp));
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||||
{
|
{
|
||||||
struct hci_conn *hcon = conn->hcon;
|
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);
|
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||||
|
|
||||||
if (!lmp_host_le_capable(hcon->hdev))
|
if (!lmp_host_le_capable(hcon->hdev))
|
||||||
return 1;
|
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)
|
if (sec_level == BT_SECURITY_LOW)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (hcon->sec_level >= sec_level)
|
if (hcon->sec_level >= sec_level)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
authreq = seclevel_to_authreq(sec_level);
|
if (hcon->link_mode & HCI_LM_MASTER)
|
||||||
|
if (smp_ltk_encrypt(conn))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
smp = smp_chan_create(conn);
|
||||||
|
|
||||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||||
struct smp_cmd_pairing cp;
|
struct smp_cmd_pairing cp;
|
||||||
struct link_key *key;
|
|
||||||
|
|
||||||
key = hci_find_link_key_type(hcon->hdev, conn->dst,
|
build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
|
||||||
HCI_LK_SMP_LTK);
|
smp->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
if (key) {
|
memcpy(&smp->preq[1], &cp, sizeof(cp));
|
||||||
struct key_master_id *master = (void *) key->data;
|
|
||||||
|
|
||||||
hci_le_start_enc(hcon, master->ediv, master->rand,
|
|
||||||
key->val);
|
|
||||||
hcon->enc_key_size = key->pin_len;
|
|
||||||
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
build_pairing_cmd(conn, &cp, NULL, authreq);
|
|
||||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
|
||||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
|
||||||
|
|
||||||
mod_timer(&conn->security_timer, jiffies +
|
|
||||||
msecs_to_jiffies(SMP_TIMEOUT));
|
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||||
} else {
|
} else {
|
||||||
struct smp_cmd_security_req cp;
|
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);
|
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
hcon->pending_sec_level = sec_level;
|
hcon->pending_sec_level = sec_level;
|
||||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -518,10 +602,11 @@ done:
|
||||||
static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
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_cmd_encrypt_info *rp = (void *) skb->data;
|
||||||
|
struct smp_chan *smp = conn->smp_chan;
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*rp));
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
memcpy(conn->tk, rp->ltk, sizeof(conn->tk));
|
memcpy(smp->tk, rp->ltk, sizeof(smp->tk));
|
||||||
|
|
||||||
return 0;
|
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)
|
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_cmd_master_ident *rp = (void *) skb->data;
|
||||||
|
struct smp_chan *smp = conn->smp_chan;
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*rp));
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size,
|
hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
|
||||||
rp->ediv, rp->rand, conn->tk);
|
rp->ediv, rp->rand, smp->tk);
|
||||||
|
|
||||||
smp_distribute_keys(conn, 1);
|
smp_distribute_keys(conn, 1);
|
||||||
|
|
||||||
|
@ -552,12 +638,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
goto done;
|
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));
|
skb_pull(skb, sizeof(code));
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
@ -621,20 +701,21 @@ done:
|
||||||
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
|
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
|
||||||
{
|
{
|
||||||
struct smp_cmd_pairing *req, *rsp;
|
struct smp_cmd_pairing *req, *rsp;
|
||||||
|
struct smp_chan *smp = conn->smp_chan;
|
||||||
__u8 *keydist;
|
__u8 *keydist;
|
||||||
|
|
||||||
BT_DBG("conn %p force %d", conn, force);
|
BT_DBG("conn %p force %d", conn, force);
|
||||||
|
|
||||||
if (IS_ERR(conn->hcon->hdev->tfm))
|
if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
|
||||||
return PTR_ERR(conn->hcon->hdev->tfm);
|
return 0;
|
||||||
|
|
||||||
rsp = (void *) &conn->prsp[1];
|
rsp = (void *) &smp->prsp[1];
|
||||||
|
|
||||||
/* The responder sends its keys first */
|
/* The responder sends its keys first */
|
||||||
if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07))
|
if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
req = (void *) &conn->preq[1];
|
req = (void *) &smp->preq[1];
|
||||||
|
|
||||||
if (conn->hcon->out) {
|
if (conn->hcon->out) {
|
||||||
keydist = &rsp->init_key_dist;
|
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);
|
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);
|
ediv, ident.rand, enc.ltk);
|
||||||
|
|
||||||
ident.ediv = cpu_to_le16(ediv);
|
ident.ediv = cpu_to_le16(ediv);
|
||||||
|
@ -698,5 +779,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
|
||||||
*keydist &= ~SMP_DIST_SIGN;
|
*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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue