diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 74e14fb33c8..7faf7fe69ed 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1034,6 +1034,7 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le, int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name); void mgmt_inquiry_started(u16 index); void mgmt_inquiry_complete_evt(u16 index, u8 status); +int mgmt_encrypt_change(u16 index, bdaddr_t *bdaddr, u8 status); /* LE SMP Management interface */ int le_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, void *cp); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index ce7ecf1a789..2d3028d7a48 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -228,6 +228,12 @@ struct mgmt_cp_set_connection_params { __le16 timeout_multiplier; } __packed; +#define MGMT_OP_ENCRYPT_LINK 0x0021 +struct mgmt_cp_encrypt_link { + bdaddr_t bdaddr; + __u8 enable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -329,3 +335,10 @@ struct mgmt_ev_user_passkey_request { bdaddr_t bdaddr; } __packed; +#define MGMT_EV_ENCRYPT_CHANGE 0x0016 +struct mgmt_ev_encrypt_change { + bdaddr_t bdaddr; + __u8 status; +} __packed; + + diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a6e34858cfa..6fdc7c73fd0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1860,6 +1860,9 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff * hci_conn_put(conn); } else hci_encrypt_cfm(conn, ev->status, ev->encrypt); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_encrypt_change(hdev->id, &conn->dst, ev->status); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0e7ff51312b..6e5d9fc92fa 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1330,6 +1330,64 @@ failed: return err; } +static int encrypt_link(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_encrypt_link *cp; + struct hci_cp_set_conn_encrypt enc; + struct hci_conn *conn; + int err = 0; + + BT_DBG(""); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENODEV); + + hci_dev_lock(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENETDOWN); + goto failed; + } + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->bdaddr); + if (!conn) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, ENOTCONN); + + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + return cmd_status(sk, index, MGMT_OP_ENCRYPT_LINK, EINPROGRESS); + + if (conn->link_mode & HCI_LM_AUTH) { + enc.handle = cpu_to_le16(conn->handle); + enc.encrypt = cp->enable; + err = hci_send_cmd(hdev, + HCI_OP_SET_CONN_ENCRYPT, sizeof(enc), &enc); + } else { + conn->auth_initiator = 1; + if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { + struct hci_cp_auth_requested cp; + cp.handle = cpu_to_le16(conn->handle); + err = hci_send_cmd(conn->hdev, + HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); + } + } + +failed: + hci_dev_unlock(hdev); + hci_dev_put(hdev); + + return err; +} + + static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, u16 len) { @@ -2317,6 +2375,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_ENCRYPT_LINK: + err = encrypt_link(sk, index, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); @@ -2828,3 +2889,17 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name) return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); } + +int mgmt_encrypt_change(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct mgmt_ev_encrypt_change ev; + + BT_DBG("hci%u", index); + + bacpy(&ev.bdaddr, bdaddr); + ev.status = status; + + return mgmt_event(MGMT_EV_ENCRYPT_CHANGE, index, &ev, sizeof(ev), + NULL); +} +