Bluetooth: Fix mgmt pairing failure when authentication fails
[deliverable/linux.git] / net / bluetooth / smp.c
index fd32943008030332bc523e12a8d2e9a560381f04..25c9040e0b1297e6ecc114d1d3405b37b707b07c 100644 (file)
 
 #include "smp.h"
 
+#define SMP_ALLOW_CMD(smp, code)       set_bit(code, &smp->allow_cmd)
+#define SMP_DISALLOW_CMD(smp, code)    clear_bit(code, &smp->allow_cmd)
+
 #define SMP_TIMEOUT    msecs_to_jiffies(30000)
 
 #define AUTH_REQ_MASK   0x07
+#define KEY_DIST_MASK  0x07
 
 enum {
        SMP_FLAG_TK_VALID,
@@ -44,7 +48,10 @@ enum {
 };
 
 struct smp_chan {
-       struct l2cap_conn *conn;
+       struct l2cap_conn       *conn;
+       struct delayed_work     security_timer;
+       unsigned long           allow_cmd; /* Bitmask of allowed commands */
+
        u8              preq[7]; /* SMP Pairing Request */
        u8              prsp[7]; /* SMP Pairing Response */
        u8              prnd[16]; /* SMP Pairing Random (local) */
@@ -139,12 +146,18 @@ static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
        return 0;
 }
 
-bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
-                    bdaddr_t *bdaddr)
+bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr)
 {
+       struct l2cap_chan *chan = hdev->smp_data;
+       struct crypto_blkcipher *tfm;
        u8 hash[3];
        int err;
 
+       if (!chan || !chan->data)
+               return false;
+
+       tfm = chan->data;
+
        BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
 
        err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
@@ -154,10 +167,17 @@ bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
        return !memcmp(bdaddr->b, hash, 3);
 }
 
-int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa)
+int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
 {
+       struct l2cap_chan *chan = hdev->smp_data;
+       struct crypto_blkcipher *tfm;
        int err;
 
+       if (!chan || !chan->data)
+               return -EOPNOTSUPP;
+
+       tfm = chan->data;
+
        get_random_bytes(&rpa->b[3], 3);
 
        rpa->b[5] &= 0x3f;      /* Clear two most significant bits */
@@ -235,47 +255,38 @@ static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
        return err;
 }
 
-static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
-                                    u16 dlen, void *data)
+static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
 {
-       struct sk_buff *skb;
-       struct l2cap_hdr *lh;
-       int len;
-
-       len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
-
-       if (len > conn->mtu)
-               return NULL;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp;
+       struct kvec iv[2];
+       struct msghdr msg;
 
-       skb = bt_skb_alloc(len, GFP_ATOMIC);
-       if (!skb)
-               return NULL;
+       if (!chan)
+               return;
 
-       lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-       lh->len = cpu_to_le16(sizeof(code) + dlen);
-       lh->cid = cpu_to_le16(L2CAP_CID_SMP);
+       BT_DBG("code 0x%2.2x", code);
 
-       memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
+       iv[0].iov_base = &code;
+       iv[0].iov_len = 1;
 
-       memcpy(skb_put(skb, dlen), data, dlen);
+       iv[1].iov_base = data;
+       iv[1].iov_len = len;
 
-       return skb;
-}
+       memset(&msg, 0, sizeof(msg));
 
-static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
-{
-       struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
+       msg.msg_iov = (struct iovec *) &iv;
+       msg.msg_iovlen = 2;
 
-       BT_DBG("code 0x%2.2x", code);
+       l2cap_chan_send(chan, &msg, 1 + len);
 
-       if (!skb)
+       if (!chan->data)
                return;
 
-       skb->priority = HCI_PRIO_MAX;
-       hci_send_acl(conn->hchan, skb, 0);
+       smp = chan->data;
 
-       cancel_delayed_work_sync(&conn->security_timer);
-       schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT);
+       cancel_delayed_work_sync(&smp->security_timer);
+       schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
 }
 
 static __u8 authreq_to_seclevel(__u8 authreq)
@@ -302,7 +313,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
                              struct smp_cmd_pairing *req,
                              struct smp_cmd_pairing *rsp, __u8 authreq)
 {
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        struct hci_conn *hcon = conn->hcon;
        struct hci_dev *hdev = hcon->hdev;
        u8 local_dist = 0, remote_dist = 0;
@@ -345,7 +357,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
 
 static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
 {
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
 
        if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
            (max_key_size < SMP_MIN_ENC_KEY_SIZE))
@@ -356,21 +369,60 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
        return 0;
 }
 
+static void smp_chan_destroy(struct l2cap_conn *conn)
+{
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
+       bool complete;
+
+       BUG_ON(!smp);
+
+       cancel_delayed_work_sync(&smp->security_timer);
+
+       complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
+       mgmt_smp_complete(conn->hcon, complete);
+
+       kfree(smp->csrk);
+       kfree(smp->slave_csrk);
+
+       crypto_free_blkcipher(smp->tfm_aes);
+
+       /* If pairing failed clean up any keys we might have */
+       if (!complete) {
+               if (smp->ltk) {
+                       list_del(&smp->ltk->list);
+                       kfree(smp->ltk);
+               }
+
+               if (smp->slave_ltk) {
+                       list_del(&smp->slave_ltk->list);
+                       kfree(smp->slave_ltk);
+               }
+
+               if (smp->remote_irk) {
+                       list_del(&smp->remote_irk->list);
+                       kfree(smp->remote_irk);
+               }
+       }
+
+       chan->data = NULL;
+       kfree(smp);
+       hci_conn_drop(conn->hcon);
+}
+
 static void smp_failure(struct l2cap_conn *conn, u8 reason)
 {
        struct hci_conn *hcon = conn->hcon;
+       struct l2cap_chan *chan = conn->smp;
 
        if (reason)
                smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
                             &reason);
 
        clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
-       mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type,
-                        HCI_ERROR_AUTH_FAILURE);
+       mgmt_auth_failed(hcon, HCI_ERROR_AUTH_FAILURE);
 
-       cancel_delayed_work_sync(&conn->security_timer);
-
-       if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
+       if (chan->data)
                smp_chan_destroy(conn);
 }
 
@@ -405,7 +457,8 @@ 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;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        u8 method;
        u32 passkey = 0;
        int ret = 0;
@@ -503,6 +556,11 @@ static u8 smp_confirm(struct smp_chan *smp)
 
        smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
 
+       if (conn->hcon->out)
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+       else
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
        return 0;
 }
 
@@ -574,82 +632,262 @@ static u8 smp_random(struct smp_chan *smp)
        return 0;
 }
 
-static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
+static void smp_notify_keys(struct l2cap_conn *conn)
 {
-       struct smp_chan *smp;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       struct smp_cmd_pairing *req = (void *) &smp->preq[1];
+       struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
+       bool persistent;
 
-       smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
-       if (!smp) {
-               clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
-               return NULL;
+       if (smp->remote_irk) {
+               mgmt_new_irk(hdev, smp->remote_irk);
+               /* Now that user space can be considered to know the
+                * identity address track the connection based on it
+                * from now on.
+                */
+               bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+               hcon->dst_type = smp->remote_irk->addr_type;
+               queue_work(hdev->workqueue, &conn->id_addr_update_work);
+
+               /* When receiving an indentity resolving key for
+                * a remote device that does not use a resolvable
+                * private address, just remove the key so that
+                * it is possible to use the controller white
+                * list for scanning.
+                *
+                * Userspace will have been told to not store
+                * this key at this point. So it is safe to
+                * just remove it.
+                */
+               if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
+                       list_del(&smp->remote_irk->list);
+                       kfree(smp->remote_irk);
+                       smp->remote_irk = NULL;
+               }
        }
 
-       smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(smp->tfm_aes)) {
-               BT_ERR("Unable to create ECB crypto context");
-               kfree(smp);
-               clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
-               return NULL;
+       /* The LTKs and CSRKs should be persistent only if both sides
+        * had the bonding bit set in their authentication requests.
+        */
+       persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+
+       if (smp->csrk) {
+               smp->csrk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->csrk->bdaddr, &hcon->dst);
+               mgmt_new_csrk(hdev, smp->csrk, persistent);
        }
 
-       smp->conn = conn;
-       conn->smp_chan = smp;
+       if (smp->slave_csrk) {
+               smp->slave_csrk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->slave_csrk->bdaddr, &hcon->dst);
+               mgmt_new_csrk(hdev, smp->slave_csrk, persistent);
+       }
 
-       hci_conn_hold(conn->hcon);
+       if (smp->ltk) {
+               smp->ltk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->ltk->bdaddr, &hcon->dst);
+               mgmt_new_ltk(hdev, smp->ltk, persistent);
+       }
 
-       return smp;
+       if (smp->slave_ltk) {
+               smp->slave_ltk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
+               mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
+       }
 }
 
-void smp_chan_destroy(struct l2cap_conn *conn)
+static void smp_allow_key_dist(struct smp_chan *smp)
 {
-       struct smp_chan *smp = conn->smp_chan;
-       bool complete;
+       /* Allow the first expected phase 3 PDU. The rest of the PDUs
+        * will be allowed in each PDU handler to ensure we receive
+        * them in the correct order.
+        */
+       if (smp->remote_key_dist & SMP_DIST_ENC_KEY)
+               SMP_ALLOW_CMD(smp, SMP_CMD_ENCRYPT_INFO);
+       else if (smp->remote_key_dist & SMP_DIST_ID_KEY)
+               SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+       else if (smp->remote_key_dist & SMP_DIST_SIGN)
+               SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+}
 
-       BUG_ON(!smp);
+static void smp_distribute_keys(struct smp_chan *smp)
+{
+       struct smp_cmd_pairing *req, *rsp;
+       struct l2cap_conn *conn = smp->conn;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       __u8 *keydist;
 
-       complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
-       mgmt_smp_complete(conn->hcon, complete);
+       BT_DBG("conn %p", conn);
 
-       kfree(smp->csrk);
-       kfree(smp->slave_csrk);
+       rsp = (void *) &smp->prsp[1];
+
+       /* The responder sends its keys first */
+       if (hcon->out && (smp->remote_key_dist & KEY_DIST_MASK)) {
+               smp_allow_key_dist(smp);
+               return;
+       }
+
+       req = (void *) &smp->preq[1];
+
+       if (hcon->out) {
+               keydist = &rsp->init_key_dist;
+               *keydist &= req->init_key_dist;
+       } else {
+               keydist = &rsp->resp_key_dist;
+               *keydist &= req->resp_key_dist;
+       }
+
+       BT_DBG("keydist 0x%x", *keydist);
+
+       if (*keydist & SMP_DIST_ENC_KEY) {
+               struct smp_cmd_encrypt_info enc;
+               struct smp_cmd_master_ident ident;
+               struct smp_ltk *ltk;
+               u8 authenticated;
+               __le16 ediv;
+               __le64 rand;
+
+               get_random_bytes(enc.ltk, sizeof(enc.ltk));
+               get_random_bytes(&ediv, sizeof(ediv));
+               get_random_bytes(&rand, sizeof(rand));
+
+               smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
+
+               authenticated = hcon->sec_level == BT_SECURITY_HIGH;
+               ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
+                                 SMP_LTK_SLAVE, authenticated, enc.ltk,
+                                 smp->enc_key_size, ediv, rand);
+               smp->slave_ltk = ltk;
+
+               ident.ediv = ediv;
+               ident.rand = rand;
+
+               smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
+
+               *keydist &= ~SMP_DIST_ENC_KEY;
+       }
+
+       if (*keydist & SMP_DIST_ID_KEY) {
+               struct smp_cmd_ident_addr_info addrinfo;
+               struct smp_cmd_ident_info idinfo;
+
+               memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk));
+
+               smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
+
+               /* The hci_conn contains the local identity address
+                * after the connection has been established.
+                *
+                * This is true even when the connection has been
+                * established using a resolvable random address.
+                */
+               bacpy(&addrinfo.bdaddr, &hcon->src);
+               addrinfo.addr_type = hcon->src_type;
+
+               smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
+                            &addrinfo);
+
+               *keydist &= ~SMP_DIST_ID_KEY;
+       }
+
+       if (*keydist & SMP_DIST_SIGN) {
+               struct smp_cmd_sign_info sign;
+               struct smp_csrk *csrk;
+
+               /* Generate a new random key */
+               get_random_bytes(sign.csrk, sizeof(sign.csrk));
+
+               csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
+               if (csrk) {
+                       csrk->master = 0x00;
+                       memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
+               }
+               smp->slave_csrk = csrk;
+
+               smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
+
+               *keydist &= ~SMP_DIST_SIGN;
+       }
+
+       /* If there are still keys to be received wait for them */
+       if (smp->remote_key_dist & KEY_DIST_MASK) {
+               smp_allow_key_dist(smp);
+               return;
+       }
+
+       set_bit(SMP_FLAG_COMPLETE, &smp->flags);
+       smp_notify_keys(conn);
+
+       smp_chan_destroy(conn);
+}
+
+static void smp_timeout(struct work_struct *work)
+{
+       struct smp_chan *smp = container_of(work, struct smp_chan,
+                                           security_timer.work);
+       struct l2cap_conn *conn = smp->conn;
+
+       BT_DBG("conn %p", conn);
+
+       hci_disconnect(conn->hcon, HCI_ERROR_REMOTE_USER_TERM);
+}
+
+static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
+{
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp;
+
+       smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
+       if (!smp)
+               return NULL;
+
+       smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(smp->tfm_aes)) {
+               BT_ERR("Unable to create ECB crypto context");
+               kfree(smp);
+               return NULL;
+       }
 
-       crypto_free_blkcipher(smp->tfm_aes);
+       smp->conn = conn;
+       chan->data = smp;
 
-       /* If pairing failed clean up any keys we might have */
-       if (!complete) {
-               if (smp->ltk) {
-                       list_del(&smp->ltk->list);
-                       kfree(smp->ltk);
-               }
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_FAIL);
 
-               if (smp->slave_ltk) {
-                       list_del(&smp->slave_ltk->list);
-                       kfree(smp->slave_ltk);
-               }
+       INIT_DELAYED_WORK(&smp->security_timer, smp_timeout);
 
-               if (smp->remote_irk) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
-               }
-       }
+       hci_conn_hold(conn->hcon);
 
-       kfree(smp);
-       conn->smp_chan = NULL;
-       hci_conn_drop(conn->hcon);
+       return smp;
 }
 
 int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_chan *chan;
        struct smp_chan *smp;
        u32 value;
+       int err;
 
        BT_DBG("");
 
-       if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
+       if (!conn)
+               return -ENOTCONN;
+
+       chan = conn->smp;
+       if (!chan)
                return -ENOTCONN;
 
-       smp = conn->smp_chan;
+       l2cap_chan_lock(chan);
+       if (!chan->data) {
+               err = -ENOTCONN;
+               goto unlock;
+       }
+
+       smp = chan->data;
 
        switch (mgmt_op) {
        case MGMT_OP_USER_PASSKEY_REPLY:
@@ -664,12 +902,16 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
        case MGMT_OP_USER_PASSKEY_NEG_REPLY:
        case MGMT_OP_USER_CONFIRM_NEG_REPLY:
                smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
-               return 0;
+               err = 0;
+               goto unlock;
        default:
                smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
-               return -EOPNOTSUPP;
+               err = -EOPNOTSUPP;
+               goto unlock;
        }
 
+       err = 0;
+
        /* If it is our turn to send Pairing Confirm, do so now */
        if (test_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) {
                u8 rsp = smp_confirm(smp);
@@ -677,12 +919,15 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
                        smp_failure(conn, rsp);
        }
 
-       return 0;
+unlock:
+       l2cap_chan_unlock(chan);
+       return err;
 }
 
 static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+       struct l2cap_chan *chan = conn->smp;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct smp_chan *smp;
        u8 key_size, auth, sec_level;
@@ -696,10 +941,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (conn->hcon->role != HCI_ROLE_SLAVE)
                return SMP_CMD_NOTSUPP;
 
-       if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+       if (!chan->data)
                smp = smp_chan_create(conn);
        else
-               smp = conn->smp_chan;
+               smp = chan->data;
 
        if (!smp)
                return SMP_UNSPECIFIED;
@@ -708,6 +953,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
            (req->auth_req & SMP_AUTH_BONDING))
                return SMP_PAIRING_NOTSUPP;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_REQ);
+
        smp->preq[0] = SMP_CMD_PAIRING_REQ;
        memcpy(&smp->preq[1], req, sizeof(*req));
        skb_pull(skb, sizeof(*req));
@@ -741,6 +988,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
 
        /* Request setup of TK */
        ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -753,7 +1001,8 @@ static u8 smp_cmd_pairing_req(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 *req, *rsp = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        u8 key_size, auth = SMP_AUTH_NONE;
        int ret;
 
@@ -765,6 +1014,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (conn->hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
+
        skb_pull(skb, sizeof(*rsp));
 
        req = (void *) &smp->preq[1];
@@ -814,20 +1065,27 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
        if (skb->len < sizeof(smp->pcnf))
                return SMP_INVALID_PARAMS;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
 
-       if (conn->hcon->out)
+       if (conn->hcon->out) {
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
-       else if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+               return 0;
+       }
+
+       if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
                return smp_confirm(smp);
        else
                set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
@@ -837,13 +1095,16 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 
 static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 {
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(smp->rrnd))
                return SMP_INVALID_PARAMS;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
        memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
        skb_pull(skb, sizeof(smp->rrnd));
 
@@ -923,9 +1184,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
                return 0;
 
-       if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
-               return 0;
-
        smp = smp_chan_create(conn);
        if (!smp)
                return SMP_UNSPECIFIED;
@@ -943,6 +1201,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        memcpy(&smp->preq[1], &cp, sizeof(cp));
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+       SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
 
        return 0;
 }
@@ -950,8 +1209,10 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_chan *chan;
        struct smp_chan *smp;
        __u8 authreq;
+       int ret;
 
        BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
 
@@ -959,6 +1220,8 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
        if (!conn)
                return 1;
 
+       chan = conn->smp;
+
        if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
                return 1;
 
@@ -972,12 +1235,19 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
                if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
                        return 0;
 
-       if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
-               return 0;
+       l2cap_chan_lock(chan);
+
+       /* If SMP is already in progress ignore this request */
+       if (chan->data) {
+               ret = 0;
+               goto unlock;
+       }
 
        smp = smp_chan_create(conn);
-       if (!smp)
-               return 1;
+       if (!smp) {
+               ret = 1;
+               goto unlock;
+       }
 
        authreq = seclevel_to_authreq(sec_level);
 
@@ -996,30 +1266,35 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
                memcpy(&smp->preq[1], &cp, sizeof(cp));
 
                smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
        } else {
                struct smp_cmd_security_req cp;
                cp.auth_req = authreq;
                smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
+               SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_REQ);
        }
 
        set_bit(SMP_FLAG_INITIATOR, &smp->flags);
+       ret = 0;
 
-       return 0;
+unlock:
+       l2cap_chan_unlock(chan);
+       return ret;
 }
 
 static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_encrypt_info *rp = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
-       /* Ignore this PDU if it wasn't requested */
-       if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
-               return 0;
+       SMP_DISALLOW_CMD(smp, SMP_CMD_ENCRYPT_INFO);
+       SMP_ALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
 
        skb_pull(skb, sizeof(*rp));
 
@@ -1031,7 +1306,8 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
 static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_master_ident *rp = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct hci_conn *hcon = conn->hcon;
        struct smp_ltk *ltk;
@@ -1042,13 +1318,13 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
-       /* Ignore this PDU if it wasn't requested */
-       if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
-               return 0;
-
        /* Mark the information as received */
        smp->remote_key_dist &= ~SMP_DIST_ENC_KEY;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
+       if (smp->remote_key_dist & SMP_DIST_ID_KEY)
+               SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+
        skb_pull(skb, sizeof(*rp));
 
        hci_dev_lock(hdev);
@@ -1057,8 +1333,8 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
                          authenticated, smp->tk, smp->enc_key_size,
                          rp->ediv, rp->rand);
        smp->ltk = ltk;
-       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
-               smp_distribute_keys(conn);
+       if (!(smp->remote_key_dist & KEY_DIST_MASK))
+               smp_distribute_keys(smp);
        hci_dev_unlock(hdev);
 
        return 0;
@@ -1067,16 +1343,16 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_ident_info *info = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
 
        BT_DBG("");
 
        if (skb->len < sizeof(*info))
                return SMP_INVALID_PARAMS;
 
-       /* Ignore this PDU if it wasn't requested */
-       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
-               return 0;
+       SMP_DISALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+       SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
 
        skb_pull(skb, sizeof(*info));
 
@@ -1089,7 +1365,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
                                   struct sk_buff *skb)
 {
        struct smp_cmd_ident_addr_info *info = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        struct hci_conn *hcon = conn->hcon;
        bdaddr_t rpa;
 
@@ -1098,13 +1375,13 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
        if (skb->len < sizeof(*info))
                return SMP_INVALID_PARAMS;
 
-       /* Ignore this PDU if it wasn't requested */
-       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
-               return 0;
-
        /* Mark the information as received */
        smp->remote_key_dist &= ~SMP_DIST_ID_KEY;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
+       if (smp->remote_key_dist & SMP_DIST_SIGN)
+               SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+
        skb_pull(skb, sizeof(*info));
 
        hci_dev_lock(hcon->hdev);
@@ -1133,7 +1410,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
                                      smp->id_addr_type, smp->irk, &rpa);
 
 distribute:
-       smp_distribute_keys(conn);
+       if (!(smp->remote_key_dist & KEY_DIST_MASK))
+               smp_distribute_keys(smp);
 
        hci_dev_unlock(hcon->hdev);
 
@@ -1143,7 +1421,8 @@ distribute:
 static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_sign_info *rp = (void *) skb->data;
-       struct smp_chan *smp = conn->smp_chan;
+       struct l2cap_chan *chan = conn->smp;
+       struct smp_chan *smp = chan->data;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct smp_csrk *csrk;
 
@@ -1152,13 +1431,11 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
-       /* Ignore this PDU if it wasn't requested */
-       if (!(smp->remote_key_dist & SMP_DIST_SIGN))
-               return 0;
-
        /* Mark the information as received */
        smp->remote_key_dist &= ~SMP_DIST_SIGN;
 
+       SMP_DISALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+
        skb_pull(skb, sizeof(*rp));
 
        hci_dev_lock(hdev);
@@ -1168,16 +1445,17 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
                memcpy(csrk->val, rp->csrk, sizeof(csrk->val));
        }
        smp->csrk = csrk;
-       if (!(smp->remote_key_dist & SMP_DIST_SIGN))
-               smp_distribute_keys(conn);
+       smp_distribute_keys(smp);
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
 {
+       struct l2cap_conn *conn = chan->conn;
        struct hci_conn *hcon = conn->hcon;
+       struct smp_chan *smp;
        __u8 code, reason;
        int err = 0;
 
@@ -1186,13 +1464,10 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
                return 0;
        }
 
-       if (skb->len < 1) {
-               kfree_skb(skb);
+       if (skb->len < 1)
                return -EILSEQ;
-       }
 
        if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
-               err = -EOPNOTSUPP;
                reason = SMP_PAIRING_NOTSUPP;
                goto done;
        }
@@ -1200,18 +1475,19 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
        code = skb->data[0];
        skb_pull(skb, sizeof(code));
 
-       /*
-        * The SMP context must be initialized for all other PDUs except
-        * pairing and security requests. If we get any other PDU when
-        * not initialized simply disconnect (done if this function
-        * returns an error).
+       smp = chan->data;
+
+       if (code > SMP_CMD_MAX)
+               goto drop;
+
+       if (smp && !test_bit(code, &smp->allow_cmd))
+               goto drop;
+
+       /* If we don't have a context the only allowed commands are
+        * pairing request and security request.
         */
-       if (code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ &&
-           !conn->smp_chan) {
-               BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
-               kfree_skb(skb);
-               return -EOPNOTSUPP;
-       }
+       if (!smp && code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ)
+               goto drop;
 
        switch (code) {
        case SMP_CMD_PAIRING_REQ:
@@ -1220,7 +1496,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 
        case SMP_CMD_PAIRING_FAIL:
                smp_failure(conn, 0);
-               reason = 0;
                err = -EPERM;
                break;
 
@@ -1262,197 +1537,217 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 
        default:
                BT_DBG("Unknown command code 0x%2.2x", code);
-
                reason = SMP_CMD_NOTSUPP;
-               err = -EOPNOTSUPP;
                goto done;
        }
 
 done:
-       if (reason)
-               smp_failure(conn, reason);
+       if (!err) {
+               if (reason)
+                       smp_failure(conn, reason);
+               kfree_skb(skb);
+       }
 
-       kfree_skb(skb);
        return err;
+
+drop:
+       BT_ERR("%s unexpected SMP command 0x%02x from %pMR", hcon->hdev->name,
+              code, &hcon->dst);
+       kfree_skb(skb);
+       return 0;
 }
 
-static void smp_notify_keys(struct l2cap_conn *conn)
+static void smp_teardown_cb(struct l2cap_chan *chan, int err)
 {
-       struct smp_chan *smp = conn->smp_chan;
-       struct hci_conn *hcon = conn->hcon;
-       struct hci_dev *hdev = hcon->hdev;
-       struct smp_cmd_pairing *req = (void *) &smp->preq[1];
-       struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
-       bool persistent;
+       struct l2cap_conn *conn = chan->conn;
 
-       if (smp->remote_irk) {
-               mgmt_new_irk(hdev, smp->remote_irk);
-               /* Now that user space can be considered to know the
-                * identity address track the connection based on it
-                * from now on.
-                */
-               bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
-               hcon->dst_type = smp->remote_irk->addr_type;
-               l2cap_conn_update_id_addr(hcon);
+       BT_DBG("chan %p", chan);
 
-               /* When receiving an indentity resolving key for
-                * a remote device that does not use a resolvable
-                * private address, just remove the key so that
-                * it is possible to use the controller white
-                * list for scanning.
-                *
-                * Userspace will have been told to not store
-                * this key at this point. So it is safe to
-                * just remove it.
-                */
-               if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
-                       smp->remote_irk = NULL;
-               }
-       }
+       if (chan->data)
+               smp_chan_destroy(conn);
 
-       /* The LTKs and CSRKs should be persistent only if both sides
-        * had the bonding bit set in their authentication requests.
-        */
-       persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+       conn->smp = NULL;
+       l2cap_chan_put(chan);
+}
 
-       if (smp->csrk) {
-               smp->csrk->bdaddr_type = hcon->dst_type;
-               bacpy(&smp->csrk->bdaddr, &hcon->dst);
-               mgmt_new_csrk(hdev, smp->csrk, persistent);
-       }
+static void smp_resume_cb(struct l2cap_chan *chan)
+{
+       struct smp_chan *smp = chan->data;
+       struct l2cap_conn *conn = chan->conn;
+       struct hci_conn *hcon = conn->hcon;
 
-       if (smp->slave_csrk) {
-               smp->slave_csrk->bdaddr_type = hcon->dst_type;
-               bacpy(&smp->slave_csrk->bdaddr, &hcon->dst);
-               mgmt_new_csrk(hdev, smp->slave_csrk, persistent);
-       }
+       BT_DBG("chan %p", chan);
 
-       if (smp->ltk) {
-               smp->ltk->bdaddr_type = hcon->dst_type;
-               bacpy(&smp->ltk->bdaddr, &hcon->dst);
-               mgmt_new_ltk(hdev, smp->ltk, persistent);
-       }
+       if (!smp)
+               return;
 
-       if (smp->slave_ltk) {
-               smp->slave_ltk->bdaddr_type = hcon->dst_type;
-               bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
-               mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
-       }
+       if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+               return;
+
+       cancel_delayed_work(&smp->security_timer);
+
+       smp_distribute_keys(smp);
 }
 
-int smp_distribute_keys(struct l2cap_conn *conn)
+static void smp_ready_cb(struct l2cap_chan *chan)
 {
-       struct smp_cmd_pairing *req, *rsp;
-       struct smp_chan *smp = conn->smp_chan;
-       struct hci_conn *hcon = conn->hcon;
-       struct hci_dev *hdev = hcon->hdev;
-       __u8 *keydist;
+       struct l2cap_conn *conn = chan->conn;
 
-       BT_DBG("conn %p", conn);
+       BT_DBG("chan %p", chan);
 
-       if (!test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
-               return 0;
+       conn->smp = chan;
+       l2cap_chan_hold(chan);
+}
 
-       rsp = (void *) &smp->prsp[1];
+static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+       int err;
 
-       /* The responder sends its keys first */
-       if (hcon->out && (smp->remote_key_dist & 0x07))
-               return 0;
+       BT_DBG("chan %p", chan);
 
-       req = (void *) &smp->preq[1];
+       err = smp_sig_channel(chan, skb);
+       if (err) {
+               struct smp_chan *smp = chan->data;
 
-       if (hcon->out) {
-               keydist = &rsp->init_key_dist;
-               *keydist &= req->init_key_dist;
-       } else {
-               keydist = &rsp->resp_key_dist;
-               *keydist &= req->resp_key_dist;
+               if (smp)
+                       cancel_delayed_work_sync(&smp->security_timer);
+
+               hci_disconnect(chan->conn->hcon, HCI_ERROR_AUTH_FAILURE);
        }
 
-       BT_DBG("keydist 0x%x", *keydist);
+       return err;
+}
 
-       if (*keydist & SMP_DIST_ENC_KEY) {
-               struct smp_cmd_encrypt_info enc;
-               struct smp_cmd_master_ident ident;
-               struct smp_ltk *ltk;
-               u8 authenticated;
-               __le16 ediv;
-               __le64 rand;
+static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan,
+                                       unsigned long hdr_len,
+                                       unsigned long len, int nb)
+{
+       struct sk_buff *skb;
 
-               get_random_bytes(enc.ltk, sizeof(enc.ltk));
-               get_random_bytes(&ediv, sizeof(ediv));
-               get_random_bytes(&rand, sizeof(rand));
+       skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
 
-               smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
+       skb->priority = HCI_PRIO_MAX;
+       bt_cb(skb)->chan = chan;
 
-               authenticated = hcon->sec_level == BT_SECURITY_HIGH;
-               ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
-                                 SMP_LTK_SLAVE, authenticated, enc.ltk,
-                                 smp->enc_key_size, ediv, rand);
-               smp->slave_ltk = ltk;
+       return skb;
+}
 
-               ident.ediv = ediv;
-               ident.rand = rand;
+static const struct l2cap_ops smp_chan_ops = {
+       .name                   = "Security Manager",
+       .ready                  = smp_ready_cb,
+       .recv                   = smp_recv_cb,
+       .alloc_skb              = smp_alloc_skb_cb,
+       .teardown               = smp_teardown_cb,
+       .resume                 = smp_resume_cb,
+
+       .new_connection         = l2cap_chan_no_new_connection,
+       .state_change           = l2cap_chan_no_state_change,
+       .close                  = l2cap_chan_no_close,
+       .defer                  = l2cap_chan_no_defer,
+       .suspend                = l2cap_chan_no_suspend,
+       .set_shutdown           = l2cap_chan_no_set_shutdown,
+       .get_sndtimeo           = l2cap_chan_no_get_sndtimeo,
+       .memcpy_fromiovec       = l2cap_chan_no_memcpy_fromiovec,
+};
 
-               smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
+static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+{
+       struct l2cap_chan *chan;
 
-               *keydist &= ~SMP_DIST_ENC_KEY;
-       }
+       BT_DBG("pchan %p", pchan);
 
-       if (*keydist & SMP_DIST_ID_KEY) {
-               struct smp_cmd_ident_addr_info addrinfo;
-               struct smp_cmd_ident_info idinfo;
+       chan = l2cap_chan_create();
+       if (!chan)
+               return NULL;
 
-               memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk));
+       chan->chan_type = pchan->chan_type;
+       chan->ops       = &smp_chan_ops;
+       chan->scid      = pchan->scid;
+       chan->dcid      = chan->scid;
+       chan->imtu      = pchan->imtu;
+       chan->omtu      = pchan->omtu;
+       chan->mode      = pchan->mode;
 
-               smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
+       BT_DBG("created chan %p", chan);
 
-               /* The hci_conn contains the local identity address
-                * after the connection has been established.
-                *
-                * This is true even when the connection has been
-                * established using a resolvable random address.
-                */
-               bacpy(&addrinfo.bdaddr, &hcon->src);
-               addrinfo.addr_type = hcon->src_type;
+       return chan;
+}
 
-               smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
-                            &addrinfo);
+static const struct l2cap_ops smp_root_chan_ops = {
+       .name                   = "Security Manager Root",
+       .new_connection         = smp_new_conn_cb,
+
+       /* None of these are implemented for the root channel */
+       .close                  = l2cap_chan_no_close,
+       .alloc_skb              = l2cap_chan_no_alloc_skb,
+       .recv                   = l2cap_chan_no_recv,
+       .state_change           = l2cap_chan_no_state_change,
+       .teardown               = l2cap_chan_no_teardown,
+       .ready                  = l2cap_chan_no_ready,
+       .defer                  = l2cap_chan_no_defer,
+       .suspend                = l2cap_chan_no_suspend,
+       .resume                 = l2cap_chan_no_resume,
+       .set_shutdown           = l2cap_chan_no_set_shutdown,
+       .get_sndtimeo           = l2cap_chan_no_get_sndtimeo,
+       .memcpy_fromiovec       = l2cap_chan_no_memcpy_fromiovec,
+};
 
-               *keydist &= ~SMP_DIST_ID_KEY;
-       }
+int smp_register(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan;
+       struct crypto_blkcipher *tfm_aes;
 
-       if (*keydist & SMP_DIST_SIGN) {
-               struct smp_cmd_sign_info sign;
-               struct smp_csrk *csrk;
+       BT_DBG("%s", hdev->name);
 
-               /* Generate a new random key */
-               get_random_bytes(sign.csrk, sizeof(sign.csrk));
+       tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm_aes)) {
+               int err = PTR_ERR(tfm_aes);
+               BT_ERR("Unable to create crypto context");
+               return err;
+       }
 
-               csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
-               if (csrk) {
-                       csrk->master = 0x00;
-                       memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
-               }
-               smp->slave_csrk = csrk;
+       chan = l2cap_chan_create();
+       if (!chan) {
+               crypto_free_blkcipher(tfm_aes);
+               return -ENOMEM;
+       }
 
-               smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
+       chan->data = tfm_aes;
 
-               *keydist &= ~SMP_DIST_SIGN;
-       }
+       l2cap_add_scid(chan, L2CAP_CID_SMP);
 
-       /* If there are still keys to be received wait for them */
-       if ((smp->remote_key_dist & 0x07))
-               return 0;
+       l2cap_chan_set_defaults(chan);
 
-       clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags);
-       cancel_delayed_work_sync(&conn->security_timer);
-       set_bit(SMP_FLAG_COMPLETE, &smp->flags);
-       smp_notify_keys(conn);
+       bacpy(&chan->src, &hdev->bdaddr);
+       chan->src_type = BDADDR_LE_PUBLIC;
+       chan->state = BT_LISTEN;
+       chan->mode = L2CAP_MODE_BASIC;
+       chan->imtu = L2CAP_DEFAULT_MTU;
+       chan->ops = &smp_root_chan_ops;
 
-       smp_chan_destroy(conn);
+       hdev->smp_data = chan;
 
        return 0;
 }
+
+void smp_unregister(struct hci_dev *hdev)
+{
+       struct l2cap_chan *chan = hdev->smp_data;
+       struct crypto_blkcipher *tfm_aes;
+
+       if (!chan)
+               return;
+
+       BT_DBG("%s chan %p", hdev->name, chan);
+
+       tfm_aes = chan->data;
+       if (tfm_aes) {
+               chan->data = NULL;
+               crypto_free_blkcipher(tfm_aes);
+       }
+
+       hdev->smp_data = NULL;
+       l2cap_chan_put(chan);
+}
This page took 0.049069 seconds and 5 git commands to generate.