2011-12-07 00:48:04

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 0/8] Bluetooth: SMP Key Exchange

Hi,

This is the newest version of SMP key exchange.

For this to work, this depends on userspace having the address type of an
LE device, so there is a functional dependency on Andre's LE Discovery
series (patches 7/9 and 8/9 from his last series more specifically).

What's new:
- hci_add_ltk() now expects hdev to be locked;

--
Cheers,


Vinicius Costa Gomes (8):
Bluetooth: Add structures for the new LTK exchange messages
Bluetooth: Add a custom type for Short Term Keys
Bluetooth: Rename smp_key_size to enc_key_size
Bluetooth: Change SMP procedures to use the new key structures
Bluetooth: Add new mgmt handlers for Long Term Keys
Bluetooth: Add support for reusing the same hci_conn for LE links
Bluetooth: Disconnect the link if encryption fails
Bluetooth: Only increase the connection sec-level if encryption is
successful

include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 34 ++++++------
include/net/bluetooth/mgmt.h | 21 +++++++
include/net/bluetooth/smp.h | 2 +-
net/bluetooth/hci_conn.c | 33 ++++++-----
net/bluetooth/hci_core.c | 107 +++++++++++++++++++++++--------------
net/bluetooth/hci_event.c | 5 ++-
net/bluetooth/l2cap_core.c | 58 ++++++++++++++-------
net/bluetooth/mgmt.c | 77 +++++++++++++++++++++++++++
net/bluetooth/smp.c | 41 +++++++-------
10 files changed, 265 insertions(+), 114 deletions(-)

--
1.7.8



2011-12-07 00:48:12

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 8/8] Bluetooth: Only increase the connection sec-level if encryption is successful

In some case, mainly with LE connections, it is possible to get at that
point with a failed encryption.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 96407cf..7a7eec5 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -849,7 +849,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
if (conn->sec_level == BT_SECURITY_SDP)
conn->sec_level = BT_SECURITY_LOW;

- if (conn->pending_sec_level > conn->sec_level)
+ if (!status && conn->pending_sec_level > conn->sec_level)
conn->sec_level = conn->pending_sec_level;

hci_proto_encrypt_cfm(conn, status, encrypt);
--
1.7.8


2011-12-07 00:48:11

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 7/8] Bluetooth: Disconnect the link if encryption fails

The most common case of error is that the encryption keys don't agree, so
the best solution is to disconnect the link and inform the user that there
was a problem during encryption.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/l2cap_core.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 9da7ebf..15d1a3b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4532,10 +4532,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)

BT_DBG("conn %p", conn);

- if (hcon->type == LE_LINK) {
+ if (hcon->type == LE_LINK && !status) {
smp_distribute_keys(conn, 0);
del_timer(&conn->security_timer);
- }
+ } else if (hcon->type == LE_LINK)
+ l2cap_conn_del(hcon, bt_to_errno(status));

read_lock(&conn->chan_lock);

--
1.7.8


2011-12-07 00:48:10

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 6/8] Bluetooth: Add support for reusing the same hci_conn for LE links

As most LE devices leave advertising mode when they enter the connected
state, we may want to "pass" that connection to other users.

The first user will be the pairing procedure, the connection is
established without an associated socket, after the pairing is
complete, userspace may want to discover via GATT what services the
newly bonded device has.

If userspace establishes the connection while the timeout still
hasn't expired, the connection will be re-used.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
net/bluetooth/hci_conn.c | 33 +++++++++++++++------------
net/bluetooth/l2cap_core.c | 53 +++++++++++++++++++++++++++++--------------
2 files changed, 54 insertions(+), 32 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b328ac6..00b86ac 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -502,26 +502,29 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
BT_DBG("%s dst %s", hdev->name, batostr(dst));

if (type == LE_LINK) {
- struct adv_entry *entry;
+ struct adv_entry *entry = NULL;

le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (le)
- return ERR_PTR(-EBUSY);
-
- entry = hci_find_adv_entry(hdev, dst);
- if (!entry)
- return ERR_PTR(-EHOSTUNREACH);
-
- le = hci_conn_add(hdev, LE_LINK, dst);
- if (!le)
- return ERR_PTR(-ENOMEM);
-
- le->dst_type = entry->bdaddr_type;
+ if (!le) {
+ entry = hci_find_adv_entry(hdev, dst);
+ if (!entry)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ le = hci_conn_add(hdev, LE_LINK, dst);
+ if (!le)
+ return ERR_PTR(-ENOMEM);
+
+ le->dst_type = entry->bdaddr_type;
+ le->pending_sec_level = sec_level;
+ le->sec_level = BT_SECURITY_LOW;
+ le->auth_type = auth_type;
+ hci_le_connect(le);
+ }

- hci_le_connect(le);
+ le->pending_sec_level = sec_level;
+ le->auth_type = auth_type;

hci_conn_hold(le);
-
return le;
}

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 014fdec..9da7ebf 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -680,10 +680,32 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}

+static void l2cap_chan_ready(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ chan->conf_state = 0;
+ __clear_chan_timer(chan);
+
+ l2cap_state_change(chan, BT_CONNECTED);
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;

+ if (conn->hcon->type == LE_LINK) {
+ l2cap_chan_ready(chan->sk);
+ return;
+ }
+
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
@@ -934,23 +956,6 @@ clean:
bh_unlock_sock(parent);
}

-static void l2cap_chan_ready(struct sock *sk)
-{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- struct sock *parent = bt_sk(sk)->parent;
-
- BT_DBG("sk %p, parent %p", sk, parent);
-
- chan->conf_state = 0;
- __clear_chan_timer(chan);
-
- l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
-
- if (parent)
- parent->sk_data_ready(parent, 0);
-}
-
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
@@ -1194,6 +1199,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
goto done;
}

+ if (hcon->type == LE_LINK) {
+ err = 0;
+
+ read_lock(&conn->chan_lock);
+ if (!list_empty(&conn->chan_l)) {
+ err = -EBUSY;
+ hci_conn_put(hcon);
+ }
+ read_unlock(&conn->chan_lock);
+
+ if (err)
+ goto done;
+ }
+
/* Update source addr of the socket */
bacpy(src, conn->src);

--
1.7.8


2011-12-07 00:48:08

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 4/8] Bluetooth: Change SMP procedures to use the new key structures

Using separated messages and list for Long Term Keys allow simplification
of the code.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 31 +++++------
net/bluetooth/hci_core.c | 105 ++++++++++++++++++++++---------------
net/bluetooth/hci_event.c | 5 ++-
net/bluetooth/mgmt.c | 6 ++
net/bluetooth/smp.c | 29 +++++-----
5 files changed, 102 insertions(+), 74 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index e34cd71..6939012 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -84,18 +84,15 @@ struct bt_uuid {
u8 svc_hint;
};

-struct key_master_id {
- __le16 ediv;
- u8 rand[8];
-} __packed;
-
-struct link_key_data {
+struct smp_ltk {
+ struct list_head list;
bdaddr_t bdaddr;
+ u8 pin_len;
u8 type;
+ u8 enc_size;
+ __le16 ediv;
+ u8 rand[8];
u8 val[16];
- u8 pin_len;
- u8 dlen;
- u8 data[0];
} __packed;

struct link_key {
@@ -104,8 +101,6 @@ struct link_key {
u8 type;
u8 val[16];
u8 pin_len;
- u8 dlen;
- u8 data[0];
};

struct oob_data {
@@ -229,6 +224,8 @@ struct hci_dev {

struct list_head link_keys;

+ struct list_head ltks;
+
struct list_head remote_oob_data;

struct list_head adv_entries;
@@ -632,12 +629,14 @@ int hci_link_keys_clear(struct hci_dev *hdev);
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len);
-struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]);
-struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 type);
-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
+int hci_smp_ltks_clear(struct hci_dev *hdev);
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]);
+struct smp_ltk *hci_find_ltk_addr(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int new_key,
+ u8 pin_len, u8 tk[16], u8 enc_size,
+ u16 ediv, u8 rand[8]);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr);

int hci_remote_oob_data_clear(struct hci_dev *hdev);
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index ce3727e..5b819cd 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1008,6 +1008,18 @@ int hci_link_keys_clear(struct hci_dev *hdev)
return 0;
}

+int hci_smp_ltks_clear(struct hci_dev *hdev)
+{
+ struct smp_ltk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->ltks, list) {
+ list_del(&k->list);
+ kfree(k);
+ }
+
+ return 0;
+}
+
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct link_key *k;
@@ -1055,41 +1067,38 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
return 0;
}

-struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
+/* If the returned key is a STK it should be free'd by the caller */
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
{
- struct link_key *k;
-
- list_for_each_entry(k, &hdev->link_keys, list) {
- struct key_master_id *id;
+ struct smp_ltk *k, *tmp;

- if (k->type != HCI_LK_SMP_LTK)
+ list_for_each_entry_safe(k, tmp, &hdev->ltks, list) {
+ if (k->ediv != ediv ||
+ memcmp(rand, k->rand, sizeof(k->rand)))
continue;

- if (k->dlen != sizeof(*id))
- continue;
+ /* The STK should only be used once, no need to keep it */
+ if (k->type == HCI_LK_SMP_STK)
+ list_del(&k->list);

- id = (void *) &k->data;
- if (id->ediv == ediv &&
- (memcmp(rand, id->rand, sizeof(id->rand)) == 0))
- return k;
+ return k;
}

return NULL;
}
EXPORT_SYMBOL(hci_find_ltk);

-struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 type)
+struct smp_ltk *hci_find_ltk_addr(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
- struct link_key *k;
+ struct smp_ltk *k;

- list_for_each_entry(k, &hdev->link_keys, list)
- if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0)
+ list_for_each_entry(k, &hdev->ltks, list)
+ if (bacmp(bdaddr, &k->bdaddr) == 0)
return k;

return NULL;
}
-EXPORT_SYMBOL(hci_find_link_key_type);
+EXPORT_SYMBOL(hci_find_ltk_addr);

int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
@@ -1146,40 +1155,31 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
return 0;
}

-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16])
+int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int new_key,
+ u8 pin_len, u8 tk[16], u8 enc_size,
+ u16 ediv, u8 rand[8])
{
- struct link_key *key, *old_key;
- struct key_master_id *id;
- u8 old_key_type;
+ struct smp_ltk *key, *old_key;

- BT_DBG("%s addr %s", hdev->name, batostr(bdaddr));
+ if (type != HCI_LK_SMP_STK && type != HCI_LK_SMP_LTK)
+ return 0;

- old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK);
- if (old_key) {
+ old_key = hci_find_ltk_addr(hdev, bdaddr);
+ if (old_key)
key = old_key;
- old_key_type = old_key->type;
- } else {
- key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC);
+ else {
+ key = kzalloc(sizeof(*key), GFP_ATOMIC);
if (!key)
return -ENOMEM;
- list_add(&key->list, &hdev->link_keys);
- old_key_type = 0xff;
+ list_add(&key->list, &hdev->ltks);
}

- key->dlen = sizeof(*id);
-
bacpy(&key->bdaddr, bdaddr);
- memcpy(key->val, ltk, sizeof(key->val));
- key->type = HCI_LK_SMP_LTK;
- key->pin_len = key_size;
-
- id = (void *) &key->data;
- id->ediv = ediv;
- memcpy(id->rand, rand, sizeof(id->rand));
-
- if (new_key)
- mgmt_new_link_key(hdev, key, old_key_type);
+ memcpy(key->val, tk, sizeof(key->val));
+ key->pin_len = pin_len;
+ key->ediv = ediv;
+ key->enc_size = enc_size;
+ memcpy(key->rand, rand, sizeof(key->rand));

return 0;
}
@@ -1200,6 +1200,23 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
return 0;
}

+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct smp_ltk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->ltks, list) {
+ if (bacmp(bdaddr, &k->bdaddr))
+ continue;
+
+ BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+
+ list_del(&k->list);
+ kfree(k);
+ }
+
+ return 0;
+}
+
/* HCI command timer function */
static void hci_cmd_timer(unsigned long arg)
{
@@ -1483,6 +1500,7 @@ int hci_register_dev(struct hci_dev *hdev)
INIT_LIST_HEAD(&hdev->uuids);

INIT_LIST_HEAD(&hdev->link_keys);
+ INIT_LIST_HEAD(&hdev->ltks);

INIT_LIST_HEAD(&hdev->remote_oob_data);

@@ -1583,6 +1601,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_blacklist_clear(hdev);
hci_uuids_clear(hdev);
hci_link_keys_clear(hdev);
+ hci_smp_ltks_clear(hdev);
hci_remote_oob_data_clear(hdev);
hci_adv_entries_clear(hdev);
hci_dev_unlock_bh(hdev);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 35cb56e..f2006e9 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3012,7 +3012,7 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
struct hci_cp_le_ltk_reply cp;
struct hci_cp_le_ltk_neg_reply neg;
struct hci_conn *conn;
- struct link_key *ltk;
+ struct smp_ltk *ltk;

BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));

@@ -3032,6 +3032,9 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,

hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);

+ if (ltk->type == HCI_LK_SMP_STK)
+ kfree(ltk);
+
hci_dev_unlock(hdev);

return;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7a23f21..49729fa 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1088,6 +1088,12 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
bacpy(&rp.bdaddr, &cp->bdaddr);
rp.status = MGMT_STATUS_FAILED;

+ err = hci_remove_ltk(hdev, &cp->bdaddr);
+ if (err < 0) {
+ err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err);
+ goto unlock;
+ }
+
err = hci_remove_link_key(hdev, &cp->bdaddr);
if (err < 0) {
rp.status = MGMT_STATUS_NOT_PAIRED;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index d943f9f..4763719 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -358,8 +358,8 @@ static void random_work(struct work_struct *work)
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);

- hci_add_ltk(hcon->hdev, 0, conn->dst, smp->enc_key_size,
- ediv, rand, stk);
+ hci_add_ltk(hcon->hdev, conn->dst, HCI_LK_SMP_STK, 0,
+ 0, stk, smp->enc_key_size, ediv, rand);
}

return;
@@ -520,12 +520,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)

static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
{
- struct link_key *key;
- struct key_master_id *master;
+ struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;

- key = hci_find_link_key_type(hcon->hdev, conn->dst,
- HCI_LK_SMP_LTK);
+ key = hci_find_ltk_addr(hcon->hdev, conn->dst);
if (!key)
return 0;

@@ -533,10 +531,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
&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;
+ hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
+ hcon->enc_key_size = key->enc_size;

return 1;

@@ -634,13 +630,17 @@ 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;
+ struct hci_dev *hdev = conn->hcon->hdev;

skb_pull(skb, sizeof(*rp));

- hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->enc_key_size,
- rp->ediv, rp->rand, smp->tk);
+ hci_dev_lock(hdev);
+ hci_add_ltk(conn->hcon->hdev, conn->dst, HCI_LK_SMP_LTK, 1,
+ conn->hcon->pin_length, smp->tk,
+ smp->enc_key_size, rp->ediv, rp->rand);

smp_distribute_keys(conn, 1);
+ hci_dev_unlock(hdev);

return 0;
}
@@ -758,8 +758,9 @@ 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, smp->enc_key_size,
- ediv, ident.rand, enc.ltk);
+ hci_add_ltk(conn->hcon->hdev, conn->src, HCI_LK_SMP_LTK, 1,
+ conn->hcon->pin_length, enc.ltk,
+ smp->enc_key_size, ediv, ident.rand);

ident.ediv = cpu_to_le16(ediv);

--
1.7.8


2011-12-07 00:48:09

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 5/8] Bluetooth: Add new mgmt handlers for Long Term Keys

This makes use of the new messages defined to exchange LTKs
between the kernel and userspace. Handlers for loading LTKs
into the kernel and for informing userspace of a new LTK are
defined.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_core.c | 6 +++
net/bluetooth/mgmt.c | 71 ++++++++++++++++++++++++++++++++++++++
3 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6939012..96407cf 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -953,6 +953,7 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);

/* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5b819cd..8d96125 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1181,6 +1181,12 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, int new_key,
key->enc_size = enc_size;
memcpy(key->rand, rand, sizeof(key->rand));

+ if (!new_key)
+ return 0;
+
+ if (type == HCI_LK_SMP_LTK)
+ mgmt_new_ltk(hdev, key, 1);
+
return 0;
}

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 49729fa..f7314d1 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2066,6 +2066,56 @@ done:
return err;
}

+static int load_long_term_keys(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_load_long_term_keys *cp;
+ u16 key_count, expected_len;
+ int i;
+
+ cp = (void *) data;
+
+ if (len < sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
+ EINVAL);
+
+ key_count = get_unaligned_le16(&cp->key_count);
+
+ expected_len = sizeof(*cp) + key_count *
+ sizeof(struct mgmt_ltk_info);
+ if (expected_len != len) {
+ BT_ERR("load_keys: expected %u bytes, got %u bytes",
+ len, expected_len);
+ return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
+ EINVAL);
+ }
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
+ ENODEV);
+
+ BT_DBG("hci%u key_count %u", index, key_count);
+
+ hci_dev_lock_bh(hdev);
+
+ hci_smp_ltks_clear(hdev);
+
+ for (i = 0; i < key_count; i++) {
+ struct mgmt_ltk_info *key = &cp->keys[i];
+
+ hci_add_ltk(hdev, &key->bdaddr, HCI_LK_SMP_LTK, 0,
+ key->pin_len, key->val, key->enc_size,
+ key->ediv, key->rand);
+ }
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return 0;
+}
+
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
unsigned char *buf;
@@ -2198,6 +2248,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
len);
break;
+ case MGMT_OP_LOAD_LONG_TERM_KEYS:
+ err = load_long_term_keys(sk, index, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode,
@@ -2353,6 +2406,24 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}

+int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+{
+ struct mgmt_ev_new_long_term_key ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.store_hint = persistent;
+ bacpy(&ev.key.bdaddr, &key->bdaddr);
+ ev.key.pin_len = key->pin_len;
+ ev.key.enc_size = key->enc_size;
+ ev.key.ediv = key->ediv;
+ memcpy(ev.key.rand, key->rand, sizeof(key->rand));
+ memcpy(ev.key.val, key->val, sizeof(key->val));
+
+ return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
+ &ev, sizeof(ev), NULL);
+}
+
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type)
{
--
1.7.8


2011-12-07 00:48:07

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 3/8] Bluetooth: Rename smp_key_size to enc_key_size

This makes clear that this is the size of the key used to
encrypt the link.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/smp.h | 2 +-
net/bluetooth/smp.c | 18 +++++++++---------
2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
index 15b97d5..2eff4de 100644
--- a/include/net/bluetooth/smp.h
+++ b/include/net/bluetooth/smp.h
@@ -123,7 +123,7 @@ struct smp_chan {
u8 rrnd[16]; /* SMP Pairing Random (remote) */
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
- u8 smp_key_size;
+ u8 enc_key_size;
struct crypto_blkcipher *tfm;
struct work_struct confirm;
struct work_struct random;
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 0b96737..d943f9f 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -227,7 +227,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
return SMP_ENC_KEY_SIZE;

- smp->smp_key_size = max_key_size;
+ smp->enc_key_size = max_key_size;

return 0;
}
@@ -332,8 +332,8 @@ static void random_work(struct work_struct *work)
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);
+ memset(stk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);

if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) {
reason = SMP_UNSPECIFIED;
@@ -341,7 +341,7 @@ static void random_work(struct work_struct *work)
}

hci_le_start_enc(hcon, ediv, rand, stk);
- hcon->enc_key_size = smp->smp_key_size;
+ hcon->enc_key_size = smp->enc_key_size;
} else {
u8 stk[16], r[16], rand[8];
__le16 ediv;
@@ -355,10 +355,10 @@ static void random_work(struct work_struct *work)
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);
+ memset(stk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);

- hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size,
+ hci_add_ltk(hcon->hdev, 0, conn->dst, smp->enc_key_size,
ediv, rand, stk);
}

@@ -637,7 +637,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)

skb_pull(skb, sizeof(*rp));

- hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
+ hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->enc_key_size,
rp->ediv, rp->rand, smp->tk);

smp_distribute_keys(conn, 1);
@@ -758,7 +758,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, smp->smp_key_size,
+ hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->enc_key_size,
ediv, ident.rand, enc.ltk);

ident.ediv = cpu_to_le16(ediv);
--
1.7.8


2011-12-07 00:48:06

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 2/8] Bluetooth: Add a custom type for Short Term Keys

These keys are just used to encrypt the link, during SMP phase 2, they should
not be stored nor reused. We use the same list as the LTKs to temporarily store
them, but as soon as they are used they are removed from the list.

Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/hci.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 67ad984..5eb236e 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -268,6 +268,7 @@ enum {
#define HCI_LK_AUTH_COMBINATION 0x05
#define HCI_LK_CHANGED_COMBINATION 0x06
/* The spec doesn't define types for SMP keys */
+#define HCI_LK_SMP_STK 0x80
#define HCI_LK_SMP_LTK 0x81
#define HCI_LK_SMP_IRK 0x82
#define HCI_LK_SMP_CSRK 0x83
--
1.7.8


2011-12-07 00:48:05

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH 1/8] Bluetooth: Add structures for the new LTK exchange messages

This defines two in the kernel side of BlueZ two new messages, one
event that will inform userspace that a new Long Term Key was
exchanged and one that will allow userspace to load LTKs into
the kernel.

Acked-by: Marcel Holtmann <[email protected]>
Signed-off-by: Vinicius Costa Gomes <[email protected]>
---
include/net/bluetooth/mgmt.h | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 3b68806..0f100fa9 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -264,6 +264,21 @@ struct mgmt_cp_user_passkey_neg_reply {
bdaddr_t bdaddr;
} __packed;

+struct mgmt_ltk_info {
+ bdaddr_t bdaddr;
+ __u8 pin_len;
+ __u8 enc_size;
+ __le16 ediv;
+ __u8 rand[8];
+ __u8 val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0023
+struct mgmt_cp_load_long_term_keys {
+ __u16 key_count;
+ struct mgmt_ltk_info keys[0];
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -363,3 +378,9 @@ struct mgmt_ev_device_unblocked {
struct mgmt_ev_user_passkey_request {
bdaddr_t bdaddr;
} __packed;
+
+#define MGMT_EV_NEW_LONG_TERM_KEY 0x0018
+struct mgmt_ev_new_long_term_key {
+ __u8 store_hint;
+ struct mgmt_ltk_info key;
+} __packed;
--
1.7.8


2012-01-30 22:38:46

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 1/8] Bluetooth: Add structures for the new LTK exchange messages

Hi Vinicius,

> This defines two new messages, one event that will inform
> userspace that a new Long Term Key was exchanged and one that
> will allow userspace to load LTKs into the kernel.
>
> Besides the information necessary for the restablishement of
> the secure link, we added some extra information: "authenticated"
> that informs if the key can be used to establish an authenticated
> link, and "master" that informs the role in that the key should
> be used.
>
> Signed-off-by: Vinicius Costa Gomes <[email protected]>
> ---
> include/net/bluetooth/mgmt.h | 22 ++++++++++++++++++++++
> 1 files changed, 22 insertions(+), 0 deletions(-)

Acked-by: Marcel Holtmann <[email protected]>

Regards

Marcel