Bluetooth: Add management support for user confirmation request
authorJohan Hedberg <johan.hedberg@nokia.com>
Sat, 19 Feb 2011 15:05:57 +0000 (12:05 -0300)
committerGustavo F. Padovan <padovan@profusion.mobi>
Mon, 21 Feb 2011 20:22:44 +0000 (17:22 -0300)
This patch adds support for the user confirmation (numeric comparison)
Secure Simple Pairing authentication method.

Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index a5f8c4684a3279b70d75449a92b5110393e5a2b3..ec6acf2f1c0bba3dfa9b6ae7f325d1b22330b824 100644 (file)
@@ -415,6 +415,17 @@ struct hci_cp_io_capability_reply {
        __u8     authentication;
 } __packed;
 
+#define HCI_OP_USER_CONFIRM_REPLY              0x042c
+struct hci_cp_user_confirm_reply {
+       bdaddr_t bdaddr;
+} __packed;
+struct hci_rp_user_confirm_reply {
+       __u8     status;
+       bdaddr_t bdaddr;
+} __packed;
+
+#define HCI_OP_USER_CONFIRM_NEG_REPLY  0x042d
+
 #define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434
 struct hci_cp_io_capability_neg_reply {
        bdaddr_t bdaddr;
@@ -936,6 +947,12 @@ struct hci_ev_io_capa_reply {
        __u8     authentication;
 } __packed;
 
+#define HCI_EV_USER_CONFIRM_REQUEST    0x33
+struct hci_ev_user_confirm_req {
+       bdaddr_t        bdaddr;
+       __le32          passkey;
+} __packed;
+
 #define HCI_EV_SIMPLE_PAIR_COMPLETE    0x36
 struct hci_ev_simple_pair_complete {
        __u8     status;
index 506f25089207d3c73c6dde264b0943421dbf7217..05f4706e6c347390717aba6fe571f45d3e36ac23 100644 (file)
@@ -762,6 +762,10 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status);
 int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr);
 int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
 int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
+int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value);
+int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
+int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
+                                                               u8 status);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
index 1d25c59be2e340af2b581b3b1275157a583659a3..52376a3295ca5315f7b0cdb433fed9b6ea4c1c58 100644 (file)
@@ -172,6 +172,19 @@ struct mgmt_rp_pair_device {
        __u8 status;
 } __packed;
 
+#define MGMT_OP_USER_CONFIRM_REPLY     0x0015
+struct mgmt_cp_user_confirm_reply {
+       __le16 index;
+       bdaddr_t bdaddr;
+} __packed;
+struct mgmt_rp_user_confirm_reply {
+       __le16 index;
+       bdaddr_t bdaddr;
+       __u8 status;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16 opcode;
@@ -239,3 +252,10 @@ struct mgmt_ev_pin_code_request {
        __le16 index;
        bdaddr_t bdaddr;
 } __packed;
+
+#define MGMT_EV_USER_CONFIRM_REQUEST   0x000F
+struct mgmt_ev_user_confirm_request {
+       __le16 index;
+       bdaddr_t bdaddr;
+       __le32 value;
+} __packed;
index 98b5764e43156e88d6f4daa725c44d40195dbea9..604c7b5fee97293312dcc0512edeeffe737cdfa9 100644 (file)
@@ -796,6 +796,29 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
        hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
 }
 
+static void hci_cc_user_confirm_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);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr,
+                                                               rp->status);
+}
+
+static void hci_cc_user_confirm_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);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr,
+                                                               rp->status);
+}
+
 static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 {
        BT_DBG("%s status 0x%x", hdev->name, status);
@@ -1728,6 +1751,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
                hci_cc_le_read_buffer_size(hdev, skb);
                break;
 
+       case HCI_OP_USER_CONFIRM_REPLY:
+               hci_cc_user_confirm_reply(hdev, skb);
+               break;
+
+       case HCI_OP_USER_CONFIRM_NEG_REPLY:
+               hci_cc_user_confirm_neg_reply(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%x", hdev->name, opcode);
                break;
@@ -2362,6 +2393,21 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
+                                                       struct sk_buff *skb)
+{
+       struct hci_ev_user_confirm_req *ev = (void *) skb->data;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (test_bit(HCI_MGMT, &hdev->flags))
+               mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey);
+
+       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;
@@ -2580,6 +2626,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_io_capa_reply_evt(hdev, skb);
                break;
 
+       case HCI_EV_USER_CONFIRM_REQUEST:
+               hci_user_confirm_request_evt(hdev, skb);
+               break;
+
        case HCI_EV_SIMPLE_PAIR_COMPLETE:
                hci_simple_pair_complete_evt(hdev, skb);
                break;
index d7fc54dcbc9e1f074dadbf9af73862326d7372c5..fdcc9742bb00cab257852cbb5753a3d27aa75869 100644 (file)
@@ -1193,6 +1193,55 @@ unlock:
        return err;
 }
 
+static int user_confirm_reply(struct sock *sk, unsigned char *data, u16 len,
+                                                               int success)
+{
+       struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+       u16 dev_id, mgmt_op, hci_op;
+       struct pending_cmd *cmd;
+       struct hci_dev *hdev;
+       int err;
+
+       BT_DBG("");
+
+       dev_id = get_unaligned_le16(&cp->index);
+
+       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;
+       }
+
+       hdev = hci_dev_get(dev_id);
+       if (!hdev)
+               return cmd_status(sk, mgmt_op, ENODEV);
+
+       if (!test_bit(HCI_UP, &hdev->flags)) {
+               err = cmd_status(sk, mgmt_op, ENETDOWN);
+               goto failed;
+       }
+
+       cmd = mgmt_pending_add(sk, mgmt_op, dev_id, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto failed;
+       }
+
+       err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr);
+       if (err < 0) {
+               list_del(&cmd->list);
+               mgmt_pending_free(cmd);
+       }
+
+failed:
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
 {
        unsigned char *buf;
@@ -1281,6 +1330,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        case MGMT_OP_PAIR_DEVICE:
                err = pair_device(sk, buf + sizeof(*hdr), len);
                break;
+       case MGMT_OP_USER_CONFIRM_REPLY:
+               err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 1);
+               break;
+       case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+               err = user_confirm_reply(sk, buf + sizeof(*hdr), len, 0);
+               break;
        default:
                BT_DBG("Unknown op %u", opcode);
                err = cmd_status(sk, opcode, 0x01);
@@ -1541,3 +1596,51 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
 
        return err;
 }
+
+int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value)
+{
+       struct mgmt_ev_user_confirm_request ev;
+
+       BT_DBG("hci%u", index);
+
+       put_unaligned_le16(index, &ev.index);
+       bacpy(&ev.bdaddr, bdaddr);
+       put_unaligned_le32(value, &ev.value);
+
+       return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, &ev, sizeof(ev), NULL);
+}
+
+static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status,
+                                                               u8 opcode)
+{
+       struct pending_cmd *cmd;
+       struct mgmt_rp_user_confirm_reply rp;
+       int err;
+
+       cmd = mgmt_pending_find(opcode, index);
+       if (!cmd)
+               return -ENOENT;
+
+       put_unaligned_le16(index, &rp.index);
+       bacpy(&rp.bdaddr, bdaddr);
+       rp.status = status;
+       err = cmd_complete(cmd->sk, opcode, &rp, sizeof(rp));
+
+       list_del(&cmd->list);
+       mgmt_pending_free(cmd);
+
+       return err;
+}
+
+int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status)
+{
+       return confirm_reply_complete(index, bdaddr, status,
+                                               MGMT_OP_USER_CONFIRM_REPLY);
+}
+
+int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
+                                                               u8 status)
+{
+       return confirm_reply_complete(index, bdaddr, status,
+                                       MGMT_OP_USER_CONFIRM_NEG_REPLY);
+}