mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
Bluetooth: Add management command for enabling Secure Connections
The support for Secure Connections need to be explicitly enabled by userspace. This is required since only userspace that can handle the new link key types should enable support for Secure Connections. This command handling is similar to how Secure Simple Pairing enabling is done. It also tracks the case when Secure Connections support is enabled via raw HCI commands. This makes sure that the host features page is updated as well. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
e98d2ce293
commit
eac83dc632
4 changed files with 141 additions and 0 deletions
|
@ -1125,6 +1125,7 @@ void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||||
u8 addr_type, u8 status);
|
u8 addr_type, u8 status);
|
||||||
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
|
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
|
||||||
void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
|
void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
|
||||||
|
void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
|
||||||
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
||||||
u8 status);
|
u8 status);
|
||||||
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
||||||
|
|
|
@ -370,6 +370,8 @@ struct mgmt_cp_set_scan_params {
|
||||||
} __packed;
|
} __packed;
|
||||||
#define MGMT_SET_SCAN_PARAMS_SIZE 4
|
#define MGMT_SET_SCAN_PARAMS_SIZE 4
|
||||||
|
|
||||||
|
#define MGMT_OP_SET_SECURE_CONN 0x002D
|
||||||
|
|
||||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||||
struct mgmt_ev_cmd_complete {
|
struct mgmt_ev_cmd_complete {
|
||||||
__le16 opcode;
|
__le16 opcode;
|
||||||
|
|
|
@ -461,6 +461,34 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
u8 status = *((u8 *) skb->data);
|
||||||
|
struct hci_cp_write_sc_support *sent;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SC_SUPPORT);
|
||||||
|
if (!sent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
if (sent->support)
|
||||||
|
hdev->features[1][0] |= LMP_HOST_SC;
|
||||||
|
else
|
||||||
|
hdev->features[1][0] &= ~LMP_HOST_SC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_MGMT, &hdev->dev_flags))
|
||||||
|
mgmt_sc_enable_complete(hdev, sent->support, status);
|
||||||
|
else if (!status) {
|
||||||
|
if (sent->support)
|
||||||
|
set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
|
||||||
|
else
|
||||||
|
clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_rp_read_local_version *rp = (void *) skb->data;
|
struct hci_rp_read_local_version *rp = (void *) skb->data;
|
||||||
|
@ -2147,6 +2175,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
hci_cc_write_ssp_mode(hdev, skb);
|
hci_cc_write_ssp_mode(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_WRITE_SC_SUPPORT:
|
||||||
|
hci_cc_write_sc_support(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case HCI_OP_READ_LOCAL_VERSION:
|
case HCI_OP_READ_LOCAL_VERSION:
|
||||||
hci_cc_read_local_version(hdev, skb);
|
hci_cc_read_local_version(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4006,6 +4006,79 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
|
||||||
|
void *data, u16 len)
|
||||||
|
{
|
||||||
|
struct mgmt_mode *cp = data;
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
u8 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
|
||||||
|
status);
|
||||||
|
|
||||||
|
if (!lmp_sc_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
|
||||||
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
|
||||||
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (!hdev_is_powered(hdev)) {
|
||||||
|
bool changed;
|
||||||
|
|
||||||
|
if (cp->val)
|
||||||
|
changed = !test_and_set_bit(HCI_SC_ENABLED,
|
||||||
|
&hdev->dev_flags);
|
||||||
|
else
|
||||||
|
changed = test_and_clear_bit(HCI_SC_ENABLED,
|
||||||
|
&hdev->dev_flags);
|
||||||
|
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
|
||||||
|
if (err < 0)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
err = new_settings(hdev, sk);
|
||||||
|
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!cp->val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = mgmt_pending_add(sk, MGMT_OP_SET_SECURE_CONN, hdev, data, len);
|
||||||
|
if (!cmd) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &cp->val);
|
||||||
|
if (err < 0) {
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
failed:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
||||||
{
|
{
|
||||||
if (key->authenticated != 0x00 && key->authenticated != 0x01)
|
if (key->authenticated != 0x00 && key->authenticated != 0x01)
|
||||||
|
@ -4134,6 +4207,7 @@ static const struct mgmt_handler {
|
||||||
{ set_bredr, false, MGMT_SETTING_SIZE },
|
{ set_bredr, false, MGMT_SETTING_SIZE },
|
||||||
{ set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
|
{ set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
|
||||||
{ set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE },
|
{ set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE },
|
||||||
|
{ set_secure_conn, false, MGMT_SETTING_SIZE },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -4917,6 +4991,38 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
|
||||||
hci_req_run(&req, NULL);
|
hci_req_run(&req, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
|
||||||
|
{
|
||||||
|
struct cmd_lookup match = { NULL, hdev };
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
u8 mgmt_err = mgmt_status(status);
|
||||||
|
|
||||||
|
if (enable && test_and_clear_bit(HCI_SC_ENABLED,
|
||||||
|
&hdev->dev_flags))
|
||||||
|
new_settings(hdev, NULL);
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
|
||||||
|
cmd_status_rsp, &mgmt_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
|
||||||
|
else
|
||||||
|
changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
|
||||||
|
settings_rsp, &match);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
new_settings(hdev, match.sk);
|
||||||
|
|
||||||
|
if (match.sk)
|
||||||
|
sock_put(match.sk);
|
||||||
|
}
|
||||||
|
|
||||||
static void sk_lookup(struct pending_cmd *cmd, void *data)
|
static void sk_lookup(struct pending_cmd *cmd, void *data)
|
||||||
{
|
{
|
||||||
struct cmd_lookup *match = data;
|
struct cmd_lookup *match = data;
|
||||||
|
|
Loading…
Reference in a new issue