2011-11-16 00:32:44

by Brian Gix

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


This patch set is nearly identical to the previous patchset, except
that I added MGMT_EV_USER_PASSKEY_REQUEST, and related functions
to make more fully symmetric with MGMT_EV_USER_CONFIRM_REQUEST.

Otherwise, more of the same, and the rest of my orriginal connects apply:

This patch set addresses all the issues that I saw raised. The
first two are the header only changes previously Acked by Marcel.

The main MGMT changes are in patch 3 and 4. Patch 3 restructures
the User Pairing input without adding any functionality. Patch 4
adds in the Passkey entry, in the style introduced in 3, reusing
the shared common code.

Patch 5 handles the Passkey Request event, and the Cmd Cmplt events
generated by the Passkey Response commands.

Patches 6 and 7 clean-up some issues in SMP that could leak memory,
and complete notification to User space of pairing failures.

In patch 8, we have the actual addition of the MITM changes for SMP,
with calls into MGMT to obtain Passkeys and/or Confirmations.

Patch 9 adds the calls back into SMP with Passkey/Confirmation results.

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


2011-11-16 00:32:53

by Brian Gix

[permalink] [raw]
Subject: [PATCH 9/9] 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 | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 8a48193..ae0eaa8 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -28,6 +28,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
@@ -1475,8 +1476,9 @@ 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);

- err = cmd_status(sk, index, mgmt_op, 0);
+ err = cmd_status(sk, index, mgmt_op, -err);
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-16 00:32:52

by Brian Gix

[permalink] [raw]
Subject: [PATCH 8/9] 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 | 223 ++++++++++++++++++++++++++++++++++----
3 files changed, 205 insertions(+), 22 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 47e5934..b034be2 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 af8dde4..6730f88 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);

@@ -396,18 +509,62 @@ 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 = conn->smp_chan;
+ u32 value;
+ u8 key[16];
+ u8 reason = 0;
+ int ret = 0;
+
+ BT_DBG("");
+
+ 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);

@@ -417,19 +574,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;
@@ -439,6 +593,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;
}

@@ -447,11 +606,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];
@@ -460,12 +622,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;
@@ -473,6 +629,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;
@@ -494,8 +666,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;
@@ -548,7 +722,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;
@@ -575,6 +749,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);

@@ -595,18 +770,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-16 00:32:51

by Brian Gix

[permalink] [raw]
Subject: [PATCH 7/9] 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 3b3726a..af8dde4 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)
@@ -653,6 +663,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;
@@ -698,8 +709,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-16 00:32:50

by Brian Gix

[permalink] [raw]
Subject: [PATCH 6/9] 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 | 8 +++++++-
1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 94e94ca..3b3726a 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -379,7 +379,13 @@ 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;
+
+ if (smp && !IS_ERR(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-16 00:32:49

by Brian Gix

[permalink] [raw]
Subject: [PATCH 5/9] 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]>
---
include/net/bluetooth/hci_core.h | 5 +++
net/bluetooth/hci_event.c | 58 ++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a67ff88..47e5934 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/hci_event.c b/net/bluetooth/hci_event.c
index 0d55d00..ac22947 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -927,6 +927,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)
{
@@ -2011,6 +2042,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;
@@ -2770,6 +2809,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;
@@ -3109,6 +3163,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-16 00:32:48

by Brian Gix

[permalink] [raw]
Subject: [PATCH 4/9] 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]>
---
net/bluetooth/mgmt.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 73 insertions(+), 1 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index b1d4f05..8a48193 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1487,7 +1487,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);

@@ -1529,6 +1537,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)
{
@@ -1971,6 +2010,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;
@@ -2327,6 +2373,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)
{
@@ -2361,6 +2419,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-11-16 00:32:47

by Brian Gix

[permalink] [raw]
Subject: [PATCH 3/9] Bluetooth: User Pairing Response restructuring

There are 4 possible User Responses to pairing requests,
and they all share the same checks and handling. This
restructures the handling of the two Confirm responses
in preperation for the second two.

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

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 5562c21..b1d4f05 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1438,28 +1438,14 @@ unlock:
return err;
}

-static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
- u16 len, int success)
+static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
+ u16 mgmt_op, u16 hci_op, __le32 passkey)
{
- struct mgmt_cp_user_confirm_reply *cp = (void *) data;
- u16 mgmt_op, hci_op;
struct pending_cmd *cmd;
struct hci_dev *hdev;
+ struct hci_conn *conn;
int err;

- BT_DBG("");
-
- if (success) {
- mgmt_op = MGMT_OP_USER_CONFIRM_REPLY;
- hci_op = HCI_OP_USER_CONFIRM_REPLY;
- } else {
- mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY;
- hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY;
- }
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, mgmt_op, EINVAL);
-
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, mgmt_op, ENODEV);
@@ -1468,26 +1454,81 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,

if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
- goto failed;
+ goto done;
}

- cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
+ /*
+ * Check for an existing ACL link, if present pair via
+ * HCI commands.
+ *
+ * If no ACL link is present, check for an LE link and if
+ * present, pair via the SMP engine.
+ *
+ * If neither ACL nor LE links are present, fail with error.
+ */
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+ if (!conn) {
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
+ if (!conn) {
+ err = cmd_status(sk, index, mgmt_op, ENOTCONN);
+ goto done;
+ }
+
+ /* Continue with pairing via SMP */
+
+ err = cmd_status(sk, index, mgmt_op, 0);
+ goto done;
+ }
+
+ cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
if (!cmd) {
err = -ENOMEM;
- goto failed;
+ goto done;
}

- err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr);
+ /* Continue with pairing via HCI */
+ err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
if (err < 0)
mgmt_pending_remove(cmd);

-failed:
+done:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);

return err;
}

+static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
+{
+ struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+
+ BT_DBG("");
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
+ EINVAL);
+
+ return user_pairing_resp(sk, index, &cp->bdaddr,
+ MGMT_OP_USER_CONFIRM_REPLY,
+ HCI_OP_USER_CONFIRM_REPLY, 0);
+}
+
+static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
+ u16 len)
+{
+ struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+
+ BT_DBG("");
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
+ EINVAL);
+
+ return user_pairing_resp(sk, index, &cp->bdaddr,
+ MGMT_OP_USER_CONFIRM_NEG_REPLY,
+ HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
+}
+
static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -1924,10 +1965,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
err = pair_device(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_USER_CONFIRM_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1);
+ err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len);
break;
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0);
+ err = user_confirm_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);
@@ -2285,7 +2327,7 @@ int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
NULL);
}

-static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status, u8 opcode)
{
struct pending_cmd *cmd;
@@ -2308,14 +2350,14 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status)
{
- return confirm_reply_complete(hdev, bdaddr, status,
+ return user_pairing_resp_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_REPLY);
}

int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status)
{
- return confirm_reply_complete(hdev, bdaddr, status,
+ return user_pairing_resp_complete(hdev, bdaddr, status,
MGMT_OP_USER_CONFIRM_NEG_REPLY);
}

--
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-16 00:32:46

by Brian Gix

[permalink] [raw]
Subject: [PATCH 2/9] Bluetooth: Add MGMT opcodes for Passkey Entry

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

diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 8b07a83..3d58d7c 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -233,6 +233,17 @@ struct mgmt_cp_set_fast_connectable {
__u8 enable;
} __packed;

+#define MGMT_OP_USER_PASSKEY_REPLY 0x0020
+struct mgmt_cp_user_passkey_reply {
+ bdaddr_t bdaddr;
+ __le32 passkey;
+} __packed;
+
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021
+struct mgmt_cp_user_passkey_neg_reply {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -327,3 +338,8 @@ struct mgmt_ev_device_blocked {
struct mgmt_ev_device_unblocked {
bdaddr_t bdaddr;
} __packed;
+
+#define MGMT_EV_USER_PASSKEY_REQUEST 0x0017
+struct mgmt_ev_user_passkey_request {
+ bdaddr_t bdaddr;
+} __packed;
--
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-16 00:32:45

by Brian Gix

[permalink] [raw]
Subject: [PATCH 1/9] Bluetooth: Add HCI defines for User Passkey entry

Signed-off-by: Brian Gix <[email protected]>
Acked-by: Marcel Holtmann <[email protected]>
---
include/net/bluetooth/hci.h | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 139ce2a..e284dd9 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -453,6 +453,14 @@ struct hci_rp_user_confirm_reply {

#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d

+#define HCI_OP_USER_PASSKEY_REPLY 0x042e
+struct hci_cp_user_passkey_reply {
+ bdaddr_t bdaddr;
+ __le32 passkey;
+} __packed;
+
+#define HCI_OP_USER_PASSKEY_NEG_REPLY 0x042f
+
#define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430
struct hci_cp_remote_oob_data_reply {
bdaddr_t bdaddr;
@@ -1076,6 +1084,11 @@ struct hci_ev_user_confirm_req {
__le32 passkey;
} __packed;

+#define HCI_EV_USER_PASSKEY_REQUEST 0x34
+struct hci_ev_user_passkey_req {
+ bdaddr_t bdaddr;
+} __packed;
+
#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
struct hci_ev_remote_oob_data_request {
bdaddr_t bdaddr;
--
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