2011-11-23 16:28:32

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 0/6] Bluetooth: Add MITM protection to LE-SMP


Mods made per Gustavo's request: Moved function Prototypes to Header
in the patch that they were created.

Also some mods to the blkcipher clean-up, and added a key NULL check
to handling of incoming SMP User Response.

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum


2011-11-23 16:28:38

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 6/6] Bluetooth: Add SMP to User Passkey and Confirm

Low Energy pairing is performed through the SMP (Security Manager Protocol)
mechanism rather than HCI.

Signed-off-by: Brian Gix <[email protected]>
---
net/bluetooth/mgmt.c | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7a23f21..4e05e24 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -29,6 +29,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
+#include <net/bluetooth/smp.h>

#define MGMT_VERSION 0
#define MGMT_REVISION 1
@@ -1606,8 +1607,15 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
}

/* Continue with pairing via SMP */
+ err = smp_user_confirm_reply(conn, mgmt_op, passkey);
+
+ if (!err)
+ err = cmd_status(sk, index, mgmt_op,
+ MGMT_STATUS_SUCCESS);
+ else
+ err = cmd_status(sk, index, mgmt_op,
+ MGMT_STATUS_FAILED);

- err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS);
goto done;
}

--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-11-23 16:28:37

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 5/6] Bluetooth: Add MITM mechanism to LE-SMP

To achive Man-In-The-Middle (MITM) level security with Low Energy,
we have to enable User Passkey Comparison. This commit modifies the
hard-coded JUST-WORKS pairing mechanism to support query via the MGMT
interface of Passkey comparison and User Confirmation.

Signed-off-by: Brian Gix <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/smp.h | 3 +
net/bluetooth/smp.c | 228 ++++++++++++++++++++++++++++++++++----
3 files changed, 210 insertions(+), 22 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index e7b2e25..4aa417c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -312,6 +312,7 @@ struct hci_conn {
struct hci_dev *hdev;
void *l2cap_data;
void *sco_data;
+ void *smp_conn;

struct hci_conn *link;

diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
index 15b97d5..43b6c49 100644
--- a/include/net/bluetooth/smp.h
+++ b/include/net/bluetooth/smp.h
@@ -124,6 +124,8 @@ struct smp_chan {
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
u8 smp_key_size;
+ u8 smp_tk_valid;
+ u8 smp_cfm_pending;
struct crypto_blkcipher *tfm;
struct work_struct confirm;
struct work_struct random;
@@ -134,6 +136,7 @@ struct smp_chan {
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_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);

void smp_chan_destroy(struct l2cap_conn *conn);

diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 0b96737..e1df0a2 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -23,6 +23,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/mgmt.h>
#include <net/bluetooth/smp.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
@@ -188,24 +189,46 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
msecs_to_jiffies(SMP_TIMEOUT));
}

+static __u8 authreq_to_seclevel(__u8 authreq)
+{
+ if (authreq & SMP_AUTH_MITM)
+ return BT_SECURITY_HIGH;
+ else
+ return BT_SECURITY_MEDIUM;
+}
+
+static __u8 seclevel_to_authreq(__u8 sec_level)
+{
+ switch (sec_level) {
+ case BT_SECURITY_HIGH:
+ return SMP_AUTH_MITM | SMP_AUTH_BONDING;
+ case BT_SECURITY_MEDIUM:
+ return SMP_AUTH_BONDING;
+ default:
+ return SMP_AUTH_NONE;
+ }
+}
+
static void build_pairing_cmd(struct l2cap_conn *conn,
struct smp_cmd_pairing *req,
struct smp_cmd_pairing *rsp,
__u8 authreq)
{
- u8 dist_keys;
+ u8 all_keys = 0;
+ u8 dist_keys = 0;

- dist_keys = 0;
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
dist_keys = SMP_DIST_ENC_KEY;
authreq |= SMP_AUTH_BONDING;
+ } else {
+ authreq &= ~SMP_AUTH_BONDING;
}

if (rsp == NULL) {
req->io_capability = conn->hcon->io_capability;
req->oob_flag = SMP_OOB_NOT_PRESENT;
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
- req->init_key_dist = dist_keys;
+ req->init_key_dist = all_keys;
req->resp_key_dist = dist_keys;
req->auth_req = authreq;
return;
@@ -214,7 +237,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
rsp->io_capability = conn->hcon->io_capability;
rsp->oob_flag = SMP_OOB_NOT_PRESENT;
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
- rsp->init_key_dist = req->init_key_dist & dist_keys;
+ rsp->init_key_dist = req->init_key_dist & all_keys;
rsp->resp_key_dist = req->resp_key_dist & dist_keys;
rsp->auth_req = authreq;
}
@@ -244,6 +267,93 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
smp_chan_destroy(conn);
}

+#define JUST_WORKS 0x00
+#define JUST_CFM 0x01
+#define REQ_PASSKEY 0x02
+#define CFM_PASSKEY 0x03
+#define REQ_OOB 0x04
+#define OVERLAP 0xFF
+
+static const u8 gen_method[5][5] = {
+ {JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY},
+ {JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY},
+ {CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY},
+ {JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM},
+ {CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP}
+};
+
+static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
+ u8 local_io, u8 remote_io)
+{
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = conn->smp_chan;
+ u8 method;
+ u32 passkey = 0;
+ int ret = 0;
+
+ /* Initialize key to JUST WORKS */
+ memset(smp->tk, 0, sizeof(smp->tk));
+ smp->smp_tk_valid = 0;
+
+ BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
+
+ /* If neither side wants MITM, use JUST WORKS */
+ /* If either side has unknown io_caps, use JUST_WORKS */
+ if (!(auth & SMP_AUTH_MITM) ||
+ local_io > SMP_IO_KEYBOARD_DISPLAY ||
+ remote_io > SMP_IO_KEYBOARD_DISPLAY) {
+ auth &= ~SMP_AUTH_MITM;
+ smp->smp_tk_valid = 1;
+ return 0;
+ }
+
+ /* MITM is now officially requested, but not required */
+ /* Determine what we need (if anything) from the agent */
+ method = gen_method[local_io][remote_io];
+
+ if (method == JUST_WORKS || method == JUST_CFM)
+ auth &= ~SMP_AUTH_MITM;
+
+ /* Don't bother confirming unbonded JUST_WORKS */
+ if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) {
+ smp->smp_tk_valid = 1;
+ return 0;
+ } else if (method == JUST_WORKS) {
+ smp->smp_tk_valid = 1;
+ return 0;
+ } else if (method == OVERLAP) {
+ if (hcon->link_mode & HCI_LM_MASTER)
+ method = CFM_PASSKEY;
+ else
+ method = REQ_PASSKEY;
+ }
+
+ if (method == CFM_PASSKEY) {
+ u8 key[16];
+ /* Generate a passkey for display. It is not valid until
+ * confirmed.
+ */
+ memset(key, 0, sizeof(key));
+ get_random_bytes(&passkey, sizeof(passkey));
+ passkey %= 1000000;
+ put_unaligned_le32(passkey, key);
+ swap128(key, smp->tk);
+ BT_DBG("PassKey: %d", passkey);
+ }
+
+ hci_dev_lock(hcon->hdev);
+
+ if (method == REQ_PASSKEY)
+ ret = mgmt_user_passkey_request(hcon->hdev, conn->dst);
+ else
+ ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
+ cpu_to_le32(passkey), 0);
+
+ hci_dev_unlock(hcon->hdev);
+
+ return ret;
+}
+
static void confirm_work(struct work_struct *work)
{
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
@@ -276,6 +386,8 @@ static void confirm_work(struct work_struct *work)
goto error;
}

+ smp->smp_cfm_pending = 0;
+
swap128(res, cp.confirm_val);
smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);

@@ -381,6 +493,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)

smp->conn = conn;
conn->smp_chan = smp;
+ conn->hcon->smp_conn = conn;

hci_conn_hold(conn->hcon);

@@ -398,18 +511,67 @@ void smp_chan_destroy(struct l2cap_conn *conn)

kfree(smp);
conn->smp_chan = NULL;
+ conn->hcon->smp_conn = NULL;
hci_conn_put(conn->hcon);
}

+int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
+{
+ struct l2cap_conn *conn = hcon->smp_conn;
+ struct smp_chan *smp;
+ u32 value;
+ u8 key[16];
+ u8 reason = 0;
+ int ret = 0;
+
+ BT_DBG("");
+
+ if (!conn)
+ return -ENOTCONN;
+
+ smp = conn->smp_chan;
+
+ switch (mgmt_op) {
+ case MGMT_OP_USER_PASSKEY_REPLY:
+ value = le32_to_cpu(passkey);
+ memset(key, 0, sizeof(key));
+ BT_DBG("PassKey: %d", value);
+ put_unaligned_le32(value, key);
+ swap128(key, smp->tk);
+ /* Fall Through */
+ case MGMT_OP_USER_CONFIRM_REPLY:
+ smp->smp_tk_valid = 1;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ /* Fall Through */
+ case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+ case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+ reason = SMP_PASSKEY_ENTRY_FAILED;
+ break;
+ }
+
+ if (reason)
+ smp_failure(conn, reason, 1);
+ else if (smp->smp_cfm_pending)
+ queue_work(hcon->hdev->workqueue, &smp->confirm);
+
+ return ret;
+}
+
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;
+ u8 auth = SMP_AUTH_NONE;
int ret;

BT_DBG("conn %p", conn);

+ if (conn->hcon->link_mode & HCI_LM_MASTER)
+ return SMP_CMD_NOTSUPP;
+
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
smp = smp_chan_create(conn);

@@ -419,19 +581,16 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));

- if (req->oob_flag)
- return SMP_OOB_NOT_AVAIL;
+ /* We didn't start the pairing, so match remote */
+ if (req->auth_req & SMP_AUTH_BONDING)
+ auth = req->auth_req;

- /* We didn't start the pairing, so no requirements */
- build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE);
+ build_pairing_cmd(conn, req, &rsp, auth);

key_size = min(req->max_key_size, rsp.max_key_size);
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;

- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
-
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
@@ -441,6 +600,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)

smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);

+ /* Request setup of TK */
+ ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
return 0;
}

@@ -449,11 +613,14 @@ 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_chan *smp = conn->smp_chan;
struct hci_dev *hdev = conn->hcon->hdev;
- u8 key_size;
+ u8 key_size, auth = SMP_AUTH_NONE;
int ret;

BT_DBG("conn %p", conn);

+ if (!(conn->hcon->link_mode & HCI_LM_MASTER))
+ return SMP_CMD_NOTSUPP;
+
skb_pull(skb, sizeof(*rsp));

req = (void *) &smp->preq[1];
@@ -462,12 +629,6 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;

- if (rsp->oob_flag)
- return SMP_OOB_NOT_AVAIL;
-
- /* Just works */
- memset(smp->tk, 0, sizeof(smp->tk));
-
ret = smp_rand(smp->prnd);
if (ret)
return SMP_UNSPECIFIED;
@@ -475,6 +636,22 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));

+ if ((req->auth_req & SMP_AUTH_BONDING) &&
+ (rsp->auth_req & SMP_AUTH_BONDING))
+ auth = SMP_AUTH_BONDING;
+
+ auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
+
+ ret = tk_request(conn, 0, auth, rsp->io_capability, req->io_capability);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ smp->smp_cfm_pending = 1;
+
+ /* Can't compose response until we have been confirmed */
+ if (!smp->smp_tk_valid)
+ return 0;
+
queue_work(hdev->workqueue, &smp->confirm);

return 0;
@@ -496,8 +673,10 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
swap128(smp->prnd, random);
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
random);
- } else {
+ } else if (smp->smp_tk_valid) {
queue_work(hdev->workqueue, &smp->confirm);
+ } else {
+ smp->smp_cfm_pending = 1;
}

return 0;
@@ -550,7 +729,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)

BT_DBG("conn %p", conn);

- hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+ hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req);

if (smp_ltk_encrypt(conn))
return 0;
@@ -577,6 +756,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
{
struct hci_conn *hcon = conn->hcon;
struct smp_chan *smp = conn->smp_chan;
+ __u8 authreq;

BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);

@@ -597,18 +777,22 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
return 0;

smp = smp_chan_create(conn);
+ if (!smp)
+ return 1;
+
+ authreq = seclevel_to_authreq(sec_level);

if (hcon->link_mode & HCI_LM_MASTER) {
struct smp_cmd_pairing cp;

- build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
+ build_pairing_cmd(conn, &cp, NULL, authreq);
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 = SMP_AUTH_NONE;
+ cp.auth_req = authreq;
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
}

--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-11-23 16:28:36

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 4/6] Bluetooth: Centralize SMP pairing failure handling

Signed-off-by: Brian Gix <[email protected]>
---
net/bluetooth/smp.c | 22 ++++++++++++++++------
1 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index b9af488..0b96737 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0;
}

+static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
+{
+ if (send)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
+ &reason);
+
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
+ mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
+ del_timer(&conn->security_timer);
+ smp_chan_destroy(conn);
+}
+
static void confirm_work(struct work_struct *work)
{
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
@@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
return;

error:
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
- smp_chan_destroy(conn);
+ smp_failure(conn, reason, 1);
}

static void random_work(struct work_struct *work)
@@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
return;

error:
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
- smp_chan_destroy(conn);
+ smp_failure(conn, reason, 1);
}

static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@@ -655,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break;

case SMP_CMD_PAIRING_FAIL:
+ smp_failure(conn, skb->data[0], 0);
reason = 0;
err = -EPERM;
break;
@@ -700,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)

done:
if (reason)
- smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
- &reason);
+ smp_failure(conn, reason, 1);

kfree_skb(skb);
return err;
--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-11-23 16:28:35

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 3/6] Bluetooth: Cleanup blkcipher on SMP termination

The blkcipher must be freed to avoid memory leak.

Signed-off-by: Brian Gix <[email protected]>
---
net/bluetooth/smp.c | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)

diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 94e94ca..b9af488 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -379,7 +379,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)

void smp_chan_destroy(struct l2cap_conn *conn)
{
- kfree(conn->smp_chan);
+ struct smp_chan *smp = conn->smp_chan;
+
+ clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+
+ if (smp->tfm)
+ crypto_free_blkcipher(smp->tfm);
+
+ kfree(smp);
+ conn->smp_chan = NULL;
hci_conn_put(conn->hcon);
}

--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-11-23 16:28:34

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 2/6] Bluetooth: Add HCI User Passkey Req Evt handling

Some MITM scenarios require handling of the User Passkey Request event,
by querying the user, and passing the response back.

Signed-off-by: Brian Gix <[email protected]>
---
net/bluetooth/hci_event.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 58 insertions(+), 0 deletions(-)

diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index dfe6fbc..980da08 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -931,6 +931,37 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}

+static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ hci_dev_lock(hdev);
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
+ rp->status);
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+ hci_dev_lock(hdev);
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
+ rp->status);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2015,6 +2046,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_user_confirm_neg_reply(hdev, skb);
break;

+ case HCI_OP_USER_PASSKEY_REPLY:
+ hci_cc_user_passkey_reply(hdev, skb);
+ break;
+
+ case HCI_OP_USER_PASSKEY_NEG_REPLY:
+ hci_cc_user_passkey_neg_reply(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
@@ -2774,6 +2813,21 @@ unlock:
hci_dev_unlock(hdev);
}

+static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_user_passkey_req *ev = (void *) skb->data;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if (test_bit(HCI_MGMT, &hdev->flags))
+ mgmt_user_passkey_request(hdev, &ev->bdaddr);
+
+ hci_dev_unlock(hdev);
+}
+
static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_simple_pair_complete *ev = (void *) skb->data;
@@ -3113,6 +3167,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_user_confirm_request_evt(hdev, skb);
break;

+ case HCI_EV_USER_PASSKEY_REQUEST:
+ hci_user_passkey_request_evt(hdev, skb);
+ break;
+
case HCI_EV_SIMPLE_PAIR_COMPLETE:
hci_simple_pair_complete_evt(hdev, skb);
break;
--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-11-23 16:28:33

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v5 1/6] Bluetooth: Add User Passkey Response handling

For some MITM protection pairing scenarios, the user is
required to enter or accept a 6 digit passkey.

Signed-off-by: Brian Gix <[email protected]>
---
include/net/bluetooth/hci_core.h | 5 +++
net/bluetooth/mgmt.c | 74 +++++++++++++++++++++++++++++++++++++-
2 files changed, 78 insertions(+), 1 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 1795257..e7b2e25 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -933,6 +933,11 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
+int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr);
+int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status);
+int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 status);
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index c06a05c..7a23f21 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1618,7 +1618,15 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
}

/* Continue with pairing via HCI */
- err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
+ if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
+ struct hci_cp_user_passkey_reply cp;
+
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.passkey = passkey;
+ err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
+ } else
+ err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
+
if (err < 0)
mgmt_pending_remove(cmd);

@@ -1660,6 +1668,37 @@ static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
}

+static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
+{
+ struct mgmt_cp_user_passkey_reply *cp = (void *) data;
+
+ BT_DBG("");
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
+ EINVAL);
+
+ return user_pairing_resp(sk, index, &cp->bdaddr,
+ MGMT_OP_USER_PASSKEY_REPLY,
+ HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
+}
+
+static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
+ u16 len)
+{
+ struct mgmt_cp_user_passkey_neg_reply *cp = (void *) data;
+
+ BT_DBG("");
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ EINVAL);
+
+ return user_pairing_resp(sk, index, &cp->bdaddr,
+ MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
+}
+
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -2117,6 +2156,13 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr),
len);
break;
+ case MGMT_OP_USER_PASSKEY_REPLY:
+ err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+ err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
case MGMT_OP_SET_LOCAL_NAME:
err = set_local_name(sk, index, buf + sizeof(*hdr), len);
break;
@@ -2477,6 +2523,18 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
NULL);
}

+int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct mgmt_ev_user_passkey_request ev;
+
+ BT_DBG("%s", hdev->name);
+
+ bacpy(&ev.bdaddr, bdaddr);
+
+ return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
+ NULL);
+}
+
static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status, u8 opcode)
{
@@ -2511,6 +2569,20 @@ int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
MGMT_OP_USER_CONFIRM_NEG_REPLY);
}

+int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
+{
+ return user_pairing_resp_complete(hdev, bdaddr, status,
+ MGMT_OP_USER_PASSKEY_REPLY);
+}
+
+int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 status)
+{
+ return user_pairing_resp_complete(hdev, bdaddr, status,
+ MGMT_OP_USER_PASSKEY_NEG_REPLY);
+}
+
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
{
struct mgmt_ev_auth_failed ev;
--
1.7.7.2

--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-12-01 23:38:40

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v5 5/6] Bluetooth: Add MITM mechanism to LE-SMP

Hi Brian,

* Brian Gix <[email protected]> [2011-12-01 10:35:30 -0800]:

> On 12/1/2011 6:11 AM, Gustavo Padovan wrote:
> >Hi Brian,
>
> >>diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
> >>index 15b97d5..43b6c49 100644
> >>--- a/include/net/bluetooth/smp.h
> >>+++ b/include/net/bluetooth/smp.h
> >>@@ -124,6 +124,8 @@ struct smp_chan {
> >> u8 pcnf[16]; /* SMP Pairing Confirm */
> >> u8 tk[16]; /* SMP Temporary Key */
> >> u8 smp_key_size;
> >>+ u8 smp_tk_valid;
> >>+ u8 smp_cfm_pending;
> >
> >Those two could be converted in a bitfield, you are using them as boolean.
> >
>
> Is there a mandate that bitfields must be manipulated/checked with
> the clear_bit/set_bit/test_bit prototypes? Or can I just use a
> single u8 to represent both boolean values (as opposed to an
> unsigned long), and use standard &=, |= and & to set/clear/check?

Please use clear/set/test_bit, it is the right way.

Gustavo

2011-12-01 18:35:30

by Brian Gix

[permalink] [raw]
Subject: Re: [PATCH-v5 5/6] Bluetooth: Add MITM mechanism to LE-SMP

On 12/1/2011 6:11 AM, Gustavo Padovan wrote:
> Hi Brian,

>> diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
>> index 15b97d5..43b6c49 100644
>> --- a/include/net/bluetooth/smp.h
>> +++ b/include/net/bluetooth/smp.h
>> @@ -124,6 +124,8 @@ struct smp_chan {
>> u8 pcnf[16]; /* SMP Pairing Confirm */
>> u8 tk[16]; /* SMP Temporary Key */
>> u8 smp_key_size;
>> + u8 smp_tk_valid;
>> + u8 smp_cfm_pending;
>
> Those two could be converted in a bitfield, you are using them as boolean.
>

Is there a mandate that bitfields must be manipulated/checked with the
clear_bit/set_bit/test_bit prototypes? Or can I just use a single u8 to
represent both boolean values (as opposed to an unsigned long), and use
standard &=, |= and & to set/clear/check?


--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum

2011-12-01 14:13:05

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v5 4/6] Bluetooth: Centralize SMP pairing failure handling

Hi Brian,

* Brian Gix <[email protected]> [2011-11-23 08:28:36 -0800]:

> Signed-off-by: Brian Gix <[email protected]>
> ---
> net/bluetooth/smp.c | 22 ++++++++++++++++------
> 1 files changed, 16 insertions(+), 6 deletions(-)
>
> diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
> index b9af488..0b96737 100644
> --- a/net/bluetooth/smp.c
> +++ b/net/bluetooth/smp.c
> @@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
> return 0;
> }
>
> +static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
> +{
> + if (send)
> + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
> + &reason);
> +
> + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
> + mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
> + del_timer(&conn->security_timer);
> + smp_chan_destroy(conn);
> +}
> +
> static void confirm_work(struct work_struct *work)
> {
> struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
> @@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
> return;
>
> error:
> - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
> - smp_chan_destroy(conn);
> + smp_failure(conn, reason, 1);
> }
>
> static void random_work(struct work_struct *work)
> @@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
> return;
>
> error:
> - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
> - smp_chan_destroy(conn);
> + smp_failure(conn, reason, 1);
> }
>
> static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
> @@ -655,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
> break;
>
> case SMP_CMD_PAIRING_FAIL:
> + smp_failure(conn, skb->data[0], 0);
> reason = 0;
> err = -EPERM;
> break;
> @@ -700,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
>
> done:
> if (reason)
> - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
> - &reason);
> + smp_failure(conn, reason, 1);
>
> kfree_skb(skb);
> return err;

Patch 1 to 4 were applied. Thanks.

Gustavo

2011-12-01 14:11:35

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v5 5/6] Bluetooth: Add MITM mechanism to LE-SMP

Hi Brian,

* Brian Gix <[email protected]> [2011-11-23 08:28:37 -0800]:

> To achive Man-In-The-Middle (MITM) level security with Low Energy,
> we have to enable User Passkey Comparison. This commit modifies the
> hard-coded JUST-WORKS pairing mechanism to support query via the MGMT
> interface of Passkey comparison and User Confirmation.
>
> Signed-off-by: Brian Gix <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> include/net/bluetooth/smp.h | 3 +
> net/bluetooth/smp.c | 228 ++++++++++++++++++++++++++++++++++----
> 3 files changed, 210 insertions(+), 22 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e7b2e25..4aa417c 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -312,6 +312,7 @@ struct hci_conn {
> struct hci_dev *hdev;
> void *l2cap_data;
> void *sco_data;
> + void *smp_conn;
>
> struct hci_conn *link;
>
> diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
> index 15b97d5..43b6c49 100644
> --- a/include/net/bluetooth/smp.h
> +++ b/include/net/bluetooth/smp.h
> @@ -124,6 +124,8 @@ struct smp_chan {
> u8 pcnf[16]; /* SMP Pairing Confirm */
> u8 tk[16]; /* SMP Temporary Key */
> u8 smp_key_size;
> + u8 smp_tk_valid;
> + u8 smp_cfm_pending;

Those two could be converted in a bitfield, you are using them as boolean.

> struct crypto_blkcipher *tfm;
> struct work_struct confirm;
> struct work_struct random;
> @@ -134,6 +136,7 @@ struct smp_chan {
> 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_distribute_keys(struct l2cap_conn *conn, __u8 force);
> +int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
>
> void smp_chan_destroy(struct l2cap_conn *conn);
>
> diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
> index 0b96737..e1df0a2 100644
> --- a/net/bluetooth/smp.c
> +++ b/net/bluetooth/smp.c
> @@ -23,6 +23,7 @@
> #include <net/bluetooth/bluetooth.h>
> #include <net/bluetooth/hci_core.h>
> #include <net/bluetooth/l2cap.h>
> +#include <net/bluetooth/mgmt.h>
> #include <net/bluetooth/smp.h>
> #include <linux/crypto.h>
> #include <linux/scatterlist.h>
> @@ -188,24 +189,46 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
> msecs_to_jiffies(SMP_TIMEOUT));
> }
>
> +static __u8 authreq_to_seclevel(__u8 authreq)
> +{
> + if (authreq & SMP_AUTH_MITM)
> + return BT_SECURITY_HIGH;
> + else
> + return BT_SECURITY_MEDIUM;
> +}
> +
> +static __u8 seclevel_to_authreq(__u8 sec_level)
> +{
> + switch (sec_level) {
> + case BT_SECURITY_HIGH:
> + return SMP_AUTH_MITM | SMP_AUTH_BONDING;
> + case BT_SECURITY_MEDIUM:
> + return SMP_AUTH_BONDING;
> + default:
> + return SMP_AUTH_NONE;
> + }
> +}
> +
> static void build_pairing_cmd(struct l2cap_conn *conn,
> struct smp_cmd_pairing *req,
> struct smp_cmd_pairing *rsp,
> __u8 authreq)
> {
> - u8 dist_keys;
> + u8 all_keys = 0;
> + u8 dist_keys = 0;
>
> - dist_keys = 0;
> if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
> dist_keys = SMP_DIST_ENC_KEY;
> authreq |= SMP_AUTH_BONDING;
> + } else {
> + authreq &= ~SMP_AUTH_BONDING;
> }
>
> if (rsp == NULL) {
> req->io_capability = conn->hcon->io_capability;
> req->oob_flag = SMP_OOB_NOT_PRESENT;
> req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
> - req->init_key_dist = dist_keys;
> + req->init_key_dist = all_keys;
> req->resp_key_dist = dist_keys;
> req->auth_req = authreq;
> return;
> @@ -214,7 +237,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
> rsp->io_capability = conn->hcon->io_capability;
> rsp->oob_flag = SMP_OOB_NOT_PRESENT;
> rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
> - rsp->init_key_dist = req->init_key_dist & dist_keys;
> + rsp->init_key_dist = req->init_key_dist & all_keys;

all_keys is always zero. What's the purpose of create it?

Gustavo