Bluetooth: Add support for Pairing features exchange
This patch implements a simple version of the SMP Pairing Features exchange procedure (Vol. 3 Part H, Section 2.3.5.1). For now, everything that would cause a Pairing Method different of Just Works to be chosen is rejected. Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@openbossa.org> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
This commit is contained in:
committed by
Gustavo F. Padovan
parent
b8e66eacab
commit
da85e5e5af
@@ -1622,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
|
|||||||
/* Encryption implies authentication */
|
/* Encryption implies authentication */
|
||||||
conn->link_mode |= HCI_LM_AUTH;
|
conn->link_mode |= HCI_LM_AUTH;
|
||||||
conn->link_mode |= HCI_LM_ENCRYPT;
|
conn->link_mode |= HCI_LM_ENCRYPT;
|
||||||
|
conn->sec_level = conn->pending_sec_level;
|
||||||
} else
|
} else
|
||||||
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
||||||
}
|
}
|
||||||
|
@@ -181,6 +181,18 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
|||||||
hci_send_acl(conn->hcon, skb, 0);
|
hci_send_acl(conn->hcon, skb, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __u8 seclevel_to_authreq(__u8 level)
|
||||||
|
{
|
||||||
|
switch (level) {
|
||||||
|
case BT_SECURITY_HIGH:
|
||||||
|
/* Right now we don't support bonding */
|
||||||
|
return SMP_AUTH_MITM;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SMP_AUTH_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||||
struct smp_cmd_pairing *cmd, __u8 authreq)
|
struct smp_cmd_pairing *cmd, __u8 authreq)
|
||||||
{
|
{
|
||||||
@@ -192,7 +204,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
|||||||
cmd->auth_req = authreq;
|
cmd->auth_req = authreq;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct smp_cmd_pairing *rp = (void *) skb->data;
|
struct smp_cmd_pairing *rp = (void *) skb->data;
|
||||||
|
|
||||||
@@ -202,12 +214,11 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
memcpy(&conn->preq[1], rp, sizeof(*rp));
|
memcpy(&conn->preq[1], rp, sizeof(*rp));
|
||||||
skb_pull(skb, sizeof(*rp));
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
rp->io_capability = 0x00;
|
if (rp->oob_flag)
|
||||||
rp->oob_flag = 0x00;
|
return SMP_OOB_NOT_AVAIL;
|
||||||
rp->max_key_size = 16;
|
|
||||||
rp->init_key_dist = 0x00;
|
/* We didn't start the pairing, so no requirements */
|
||||||
rp->resp_key_dist = 0x00;
|
build_pairing_cmd(conn, rp, SMP_AUTH_NONE);
|
||||||
rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM);
|
|
||||||
|
|
||||||
/* Just works */
|
/* Just works */
|
||||||
memset(conn->tk, 0, sizeof(conn->tk));
|
memset(conn->tk, 0, sizeof(conn->tk));
|
||||||
@@ -216,9 +227,11 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
memcpy(&conn->prsp[1], rp, sizeof(*rp));
|
memcpy(&conn->prsp[1], rp, sizeof(*rp));
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct smp_cmd_pairing *rp = (void *) skb->data;
|
struct smp_cmd_pairing *rp = (void *) skb->data;
|
||||||
struct smp_cmd_pairing_confirm cp;
|
struct smp_cmd_pairing_confirm cp;
|
||||||
@@ -228,29 +241,34 @@ static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
|
if (rp->oob_flag)
|
||||||
|
return SMP_OOB_NOT_AVAIL;
|
||||||
|
|
||||||
/* Just works */
|
/* Just works */
|
||||||
memset(conn->tk, 0, sizeof(conn->tk));
|
memset(conn->tk, 0, sizeof(conn->tk));
|
||||||
|
|
||||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||||
memcpy(&conn->prsp[1], rp, sizeof(*rp));
|
memcpy(&conn->prsp[1], rp, sizeof(*rp));
|
||||||
skb_pull(skb, sizeof(*rp));
|
|
||||||
|
|
||||||
ret = smp_rand(conn->prnd);
|
ret = smp_rand(conn->prnd);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
||||||
conn->src, conn->hcon->dst_type, conn->dst, res);
|
conn->src, conn->hcon->dst_type, conn->dst, res);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
swap128(res, cp.confirm_val);
|
swap128(res, cp.confirm_val);
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smp_cmd_pairing_confirm(struct l2cap_conn *conn,
|
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
struct sk_buff *skb)
|
|
||||||
{
|
{
|
||||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||||
|
|
||||||
@@ -272,21 +290,23 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
ret = smp_rand(conn->prnd);
|
ret = smp_rand(conn->prnd);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
|
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
|
||||||
conn->hcon->dst_type, conn->dst,
|
conn->hcon->dst_type, conn->dst,
|
||||||
0, conn->src, res);
|
0, conn->src, res);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return SMP_CONFIRM_FAILED;
|
||||||
|
|
||||||
swap128(res, cp.confirm_val);
|
swap128(res, cp.confirm_val);
|
||||||
|
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_conn *hcon = conn->hcon;
|
struct hci_conn *hcon = conn->hcon;
|
||||||
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
||||||
@@ -307,19 +327,15 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
conn->hcon->dst_type, conn->dst, 0, conn->src,
|
conn->hcon->dst_type, conn->dst, 0, conn->src,
|
||||||
res);
|
res);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||||
|
|
||||||
swap128(res, confirm);
|
swap128(res, confirm);
|
||||||
|
|
||||||
if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
|
if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
|
||||||
struct smp_cmd_pairing_fail cp;
|
|
||||||
|
|
||||||
BT_ERR("Pairing failed (confirmation values mismatch)");
|
BT_ERR("Pairing failed (confirmation values mismatch)");
|
||||||
cp.reason = SMP_CONFIRM_FAILED;
|
return SMP_CONFIRM_FAILED;
|
||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(cp), &cp);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->hcon->out) {
|
if (conn->hcon->out) {
|
||||||
@@ -341,9 +357,11 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
||||||
swap128(key, hcon->ltk);
|
swap128(key, hcon->ltk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct smp_cmd_security_req *rp = (void *) skb->data;
|
struct smp_cmd_security_req *rp = (void *) skb->data;
|
||||||
struct smp_cmd_pairing cp;
|
struct smp_cmd_pairing cp;
|
||||||
@@ -352,17 +370,12 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
skb_pull(skb, sizeof(*rp));
|
skb_pull(skb, sizeof(*rp));
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
|
|
||||||
cp.io_capability = 0x00;
|
memset(&cp, 0, sizeof(cp));
|
||||||
cp.oob_flag = 0x00;
|
build_pairing_cmd(conn, &cp, rp->auth_req);
|
||||||
cp.max_key_size = 16;
|
|
||||||
cp.init_key_dist = 0x00;
|
|
||||||
cp.resp_key_dist = 0x00;
|
|
||||||
cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM);
|
|
||||||
|
|
||||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||||
@@ -370,18 +383,8 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||||
|
|
||||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||||
}
|
|
||||||
|
|
||||||
static __u8 seclevel_to_authreq(__u8 level)
|
return 0;
|
||||||
{
|
|
||||||
switch (level) {
|
|
||||||
case BT_SECURITY_HIGH:
|
|
||||||
/* For now we don't support bonding */
|
|
||||||
return SMP_AUTH_MITM;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return SMP_AUTH_NONE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||||
@@ -407,13 +410,8 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
|||||||
|
|
||||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||||
struct smp_cmd_pairing cp;
|
struct smp_cmd_pairing cp;
|
||||||
cp.io_capability = 0x00;
|
|
||||||
cp.oob_flag = 0x00;
|
|
||||||
cp.max_key_size = 16;
|
|
||||||
cp.init_key_dist = 0x00;
|
|
||||||
cp.resp_key_dist = 0x00;
|
|
||||||
cp.auth_req = authreq;
|
|
||||||
|
|
||||||
|
build_pairing_cmd(conn, &cp, authreq);
|
||||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||||
|
|
||||||
@@ -446,26 +444,28 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case SMP_CMD_PAIRING_REQ:
|
case SMP_CMD_PAIRING_REQ:
|
||||||
smp_cmd_pairing_req(conn, skb);
|
reason = smp_cmd_pairing_req(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_PAIRING_FAIL:
|
case SMP_CMD_PAIRING_FAIL:
|
||||||
|
reason = 0;
|
||||||
|
err = -EPERM;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_PAIRING_RSP:
|
case SMP_CMD_PAIRING_RSP:
|
||||||
smp_cmd_pairing_rsp(conn, skb);
|
reason = smp_cmd_pairing_rsp(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_SECURITY_REQ:
|
case SMP_CMD_SECURITY_REQ:
|
||||||
smp_cmd_security_req(conn, skb);
|
reason = smp_cmd_security_req(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_PAIRING_CONFIRM:
|
case SMP_CMD_PAIRING_CONFIRM:
|
||||||
smp_cmd_pairing_confirm(conn, skb);
|
reason = smp_cmd_pairing_confirm(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_PAIRING_RANDOM:
|
case SMP_CMD_PAIRING_RANDOM:
|
||||||
smp_cmd_pairing_random(conn, skb);
|
reason = smp_cmd_pairing_random(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMP_CMD_ENCRYPT_INFO:
|
case SMP_CMD_ENCRYPT_INFO:
|
||||||
|
Reference in New Issue
Block a user