2011-11-16 21:53:11

by Brian Gix

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



This version of the patch-set accomplishes two things:

1. It has been rebased, picking up Johan's changes to MGMT error codes,
and making coresponding matching changes in the error codes this
code in these patches return as well.

2. Splits what *was* the 3rd patch of the old set, into patches
2 and 3 of this set: Patch 2 restructures, Patch 3 adds the
code to recognize the difference between LE and BR/EDR connections.
This refactoring is per Gustavo's request.

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


2011-11-22 17:47:21

by Brian Gix

[permalink] [raw]
Subject: Re: [PATCH-v4 6/9] Bluetooth: Cleanup blkcipher on SMP termination

Hi Gustavo

On 11/21/2011 7:48 AM, Gustavo Padovan wrote:
[...]
>> 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);
>
> smp->tfm doesn't say what you want, if its allocation failed its value is
> still 0. And I don't think we need to check for smp == NULL. It can't be null
> at this point.
>
> Gustavo

OK, so I see that smp_>tfm will never equal an Error, so that check is
meaningless.

However, there are conditions where it can in fact be NULL,
particularily if the user aborts the paring process, and looking at the
blkcipher's FREE code, I am not confident that it couldn't get past the
"unlikely" NULL checks with all compilation parameters, so we should
keep at least a NULL check here.

As far as NULL checking "smp", I think we can avoid it, but only if we
make sure that we clear the HCI_CONN_LE_SMP_PEND bit within the destroy
function itself. A cursory look at the usage doesn't fill me with
confidence that the flag bit, and the allocation are rigorously kept in
sync.

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

2011-11-21 15:58:53

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 3/9] Bluetooth: Differentiate LE User Pairing Responses

Hi Brian,

* Brian Gix <[email protected]> [2011-11-16 13:53:14 -0800]:

> Low Energy (LE) pairing responses must be recognized and handled
> differently from BR/EDR pairing responses. BR/EDR responses are
> handled via HCI commands by the LMP layer, and LE responses are
> handled by the Host.
>
> Signed-off-by: Brian Gix <[email protected]>
> ---
> net/bluetooth/mgmt.c | 24 ++++++++++++++++++++++++
> 1 files changed, 24 insertions(+), 0 deletions(-)

Applied, thanks.

Gustavo

2011-11-21 15:58:25

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 2/9] Bluetooth: User Pairing Response restructuring

Hi Brian,

* Brian Gix <[email protected]> [2011-11-16 13:53:13 -0800]:

> 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 | 76 +++++++++++++++++++++++++++++++-------------------
> 1 files changed, 47 insertions(+), 29 deletions(-)

Applied, thanks.

Gustavo

2011-11-21 15:48:30

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 6/9] Bluetooth: Cleanup blkcipher on SMP termination

Hi Brian,

* Brian Gix <[email protected]> [2011-11-16 13:53:17 -0800]:

> 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);

smp->tfm doesn't say what you want, if its allocation failed its value is
still 0. And I don't think we need to check for smp == NULL. It can't be null
at this point.

Gustavo

2011-11-21 15:35:39

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 5/9] Bluetooth: Add HCI User Passkey Req Evt handling

Hi Vinicius,

* Vinicius Costa Gomes <[email protected]> [2011-11-16 20:47:07 -0300]:

> Hi Brian,
>
> Sorry if these comments appear so late in this series life.
>
> On 13:53 Wed 16 Nov, Brian Gix wrote:
> > 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 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/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);
>
> I was with the impression that the check for the MGMT bit was being
> ignored on purpose on newer code, because mgmt is going to be the
> default some time in the (near) future and if there's no one listening
> for those events the overhead should be minimal. Can anyone confirm this
> impression?

I'm ok with the checks, we can remove all of them when mgmt becomes default.

Gustavo

2011-11-21 15:32:52

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 4/9] Bluetooth: Add User Passkey Response handling

Hi Brian,

* Brian Gix <[email protected]> [2011-11-16 13:53:15 -0800]:

> 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 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);
> +}
> +

You have to export these functions in hci_core.h right here in this commit.

Gustavo

2011-11-17 00:57:16

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH-v4 1/9] Bluetooth: Add MGMT event for Passkey Entry

Hi Brian,

* Brian Gix <[email protected]> [2011-11-16 13:53:12 -0800]:

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

patch has been applied, thanks.

Gustavo

2011-11-17 00:15:00

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [PATCH-v4 8/9] Bluetooth: Add MITM mechanism to LE-SMP

Hi Brian,

On 13:53 Wed 16 Nov, Brian Gix wrote:
> 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 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;

I think that the smp_ prefix can be omitted here and loose little to
no information. There's even a patch in the mailing list that removes
the smp_ from smp_key_size. And I would rename tk_valid to valid_tk and
cfm_pending to pending_cfm. But this may be only that my brain finds it
easier to parse.

> 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);

The name of the function can be ommited, you can configure the dynamic
debugging infrastructure to print it for you.

> +
> + /* 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
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

Cheers,
--
Vinicius

2011-11-16 23:47:07

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [PATCH-v4 5/9] Bluetooth: Add HCI User Passkey Req Evt handling

Hi Brian,

Sorry if these comments appear so late in this series life.

On 13:53 Wed 16 Nov, Brian Gix wrote:
> 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 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/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);

I was with the impression that the check for the MGMT bit was being
ignored on purpose on newer code, because mgmt is going to be the
default some time in the (near) future and if there's no one listening
for those events the overhead should be minimal. Can anyone confirm this
impression?

> +
> + 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
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Vinicius

2011-11-16 21:53:20

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 | 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-16 21:53:19

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 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 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 21:53:18

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 21:53:17

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 21:53:16

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 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/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-16 21:53:15

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 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 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-11-16 21:53:14

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 3/9] Bluetooth: Differentiate LE User Pairing Responses

Low Energy (LE) pairing responses must be recognized and handled
differently from BR/EDR pairing responses. BR/EDR responses are
handled via HCI commands by the LMP layer, and LE responses are
handled by the Host.

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

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 394222e..c06a05c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1587,6 +1587,30 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
goto done;
}

+ /*
+ * 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,
+ MGMT_STATUS_NOT_CONNECTED);
+ goto done;
+ }
+
+ /* Continue with pairing via SMP */
+
+ err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS);
+ goto done;
+ }
+
cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
if (!cmd) {
err = -ENOMEM;
--
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 21:53:13

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 2/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 | 76 +++++++++++++++++++++++++++++++-------------------
1 files changed, 47 insertions(+), 29 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 1ae14c9..394222e 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1567,29 +1567,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,
- MGMT_STATUS_INVALID_PARAMS);
-
hdev = hci_dev_get(index);
if (!hdev)
return cmd_status(sk, index, mgmt_op,
@@ -1598,27 +1583,59 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
hci_dev_lock_bh(hdev);

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

- cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len);
+ 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,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ 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,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ 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)
{
@@ -2070,10 +2087,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);
@@ -2435,7 +2453,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;
@@ -2458,14 +2476,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, mgmt_status(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, mgmt_status(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 21:53:12

by Brian Gix

[permalink] [raw]
Subject: [PATCH-v4 1/9] Bluetooth: Add MGMT event for Passkey Entry

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

diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 139610e..3b68806 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -358,3 +358,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