summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2020-04-17 10:48:46 -0700
committerDavid S. Miller <davem@davemloft.net>2020-04-17 10:48:46 -0700
commit513a24ffb3f96a1eedc1f346616814f55ecb11da (patch)
tree8c9f15eb31db9f4939af76a7cc03735ecd4fb365 /net
parent2fcd80144b93ff90836a44f2054b4d82133d3a85 (diff)
parent7edc9079540b65026f3d3386b3642d1820d5fed5 (diff)
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2020-04-17 Here's the first bluetooth-next pull request for the 5.8 kernel: - Added debugfs option to control MITM flag usage during pairing - Added new BT_MODE socket option - Added support for Qualcom QCA6390 device - Added support for Realtek RTL8761B device - Added support for mSBC audio codec over USB endpoints - Added framework for Microsoft HCI vendor extensions - Added new Read Security Information management command - Fixes/cleanup to link layer privacy related code - Various other smaller cleanups & fixes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/Kconfig23
-rw-r--r--net/bluetooth/Makefile1
-rw-r--r--net/bluetooth/hci_conn.c25
-rw-r--r--net/bluetooth/hci_core.c131
-rw-r--r--net/bluetooth/hci_debugfs.c46
-rw-r--r--net/bluetooth/hci_event.c54
-rw-r--r--net/bluetooth/hci_request.c4
-rw-r--r--net/bluetooth/l2cap_sock.c138
-rw-r--r--net/bluetooth/mgmt.c55
-rw-r--r--net/bluetooth/msft.c141
-rw-r--r--net/bluetooth/msft.h18
-rw-r--r--net/bluetooth/smp.c33
12 files changed, 581 insertions, 88 deletions
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 165148c7c4ce..9e25c6570170 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -93,6 +93,21 @@ config BT_LEDS
This option selects a few LED triggers for different
Bluetooth events.
+config BT_MSFTEXT
+ bool "Enable Microsoft extensions"
+ depends on BT
+ help
+ This options enables support for the Microsoft defined HCI
+ vendor extensions.
+
+config BT_DEBUGFS
+ bool "Export Bluetooth internals in debugfs"
+ depends on BT && DEBUG_FS
+ default y
+ help
+ Provide extensive information about internal Bluetooth states
+ in debugfs.
+
config BT_SELFTEST
bool "Bluetooth self testing support"
depends on BT && DEBUG_KERNEL
@@ -120,12 +135,4 @@ config BT_SELFTEST_SMP
Run test cases for SMP cryptographic functionality, including both
legacy SMP as well as the Secure Connections features.
-config BT_DEBUGFS
- bool "Export Bluetooth internals in debugfs"
- depends on BT && DEBUG_FS
- default y
- help
- Provide extensive information about internal Bluetooth states
- in debugfs.
-
source "drivers/bluetooth/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index fda41c0b4781..41dd541a44a5 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -19,5 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
bluetooth-$(CONFIG_BT_LEDS) += leds.o
+bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index e245bc155cc2..07c34c55fc50 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -122,8 +122,18 @@ static void hci_conn_cleanup(struct hci_conn *conn)
hci_conn_hash_del(hdev, conn);
- if (hdev->notify)
- hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+ if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
+ switch (conn->setting & SCO_AIRMODE_MASK) {
+ case SCO_AIRMODE_CVSD:
+ case SCO_AIRMODE_TRANSP:
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
+ break;
+ }
+ } else {
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+ }
hci_conn_del_sysfs(conn);
@@ -577,8 +587,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
hci_dev_hold(hdev);
hci_conn_hash_add(hdev, conn);
- if (hdev->notify)
- hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
+
+ /* The SCO and eSCO connections will only be notified when their
+ * setup has been completed. This is different to ACL links which
+ * can be notified right away.
+ */
+ if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
+ }
hci_conn_init_sysfs(conn);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2e7bc2da8371..51d399273276 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -44,6 +44,7 @@
#include "hci_debugfs.h"
#include "smp.h"
#include "leds.h"
+#include "msft.h"
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
@@ -637,6 +638,14 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
events[0] |= 0x40; /* LE Data Length Change */
+ /* If the controller supports LL Privacy feature, enable
+ * the corresponding event.
+ */
+ if (hdev->le_features[0] & HCI_LE_LL_PRIVACY)
+ events[1] |= 0x02; /* LE Enhanced Connection
+ * Complete
+ */
+
/* If the controller supports Extended Scanner Filter
* Policies, enable the correspondig event.
*/
@@ -710,14 +719,6 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
* Report
*/
- /* If the controller supports the LE Extended Create Connection
- * command, enable the corresponding event.
- */
- if (use_ext_conn(hdev))
- events[1] |= 0x02; /* LE Enhanced Connection
- * Complete
- */
-
/* If the controller supports the LE Extended Advertising
* command, enable the corresponding event.
*/
@@ -826,6 +827,10 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
if (hdev->commands[29] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL);
+ /* Read local pairing options if the HCI command is supported */
+ if (hdev->commands[41] & 0x08)
+ hci_req_add(req, HCI_OP_READ_LOCAL_PAIRING_OPTS, 0, NULL);
+
/* Get MWS transport configuration if the HCI command is supported */
if (hdev->commands[30] & 0x08)
hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL);
@@ -1563,6 +1568,8 @@ setup_failed:
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
ret = hdev->set_diag(hdev, true);
+ msft_do_open(hdev);
+
clear_bit(HCI_INIT, &hdev->flags);
if (!ret) {
@@ -1758,6 +1765,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
hci_sock_dev_event(hdev, HCI_DEV_DOWN);
+ msft_do_close(hdev);
+
if (hdev->flush)
hdev->flush(hdev);
@@ -4240,6 +4249,54 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
}
}
+/* Schedule SCO */
+static void hci_sched_sco(struct hci_dev *hdev)
+{
+ struct hci_conn *conn;
+ struct sk_buff *skb;
+ int quote;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hci_conn_num(hdev, SCO_LINK))
+ return;
+
+ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+ hci_send_frame(hdev, skb);
+
+ conn->sent++;
+ if (conn->sent == ~0)
+ conn->sent = 0;
+ }
+ }
+}
+
+static void hci_sched_esco(struct hci_dev *hdev)
+{
+ struct hci_conn *conn;
+ struct sk_buff *skb;
+ int quote;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hci_conn_num(hdev, ESCO_LINK))
+ return;
+
+ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
+ &quote))) {
+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ BT_DBG("skb %p len %d", skb, skb->len);
+ hci_send_frame(hdev, skb);
+
+ conn->sent++;
+ if (conn->sent == ~0)
+ conn->sent = 0;
+ }
+ }
+}
+
static void hci_sched_acl_pkt(struct hci_dev *hdev)
{
unsigned int cnt = hdev->acl_cnt;
@@ -4271,6 +4328,10 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
hdev->acl_cnt--;
chan->sent++;
chan->conn->sent++;
+
+ /* Send pending SCO packets right away */
+ hci_sched_sco(hdev);
+ hci_sched_esco(hdev);
}
}
@@ -4355,54 +4416,6 @@ static void hci_sched_acl(struct hci_dev *hdev)
}
}
-/* Schedule SCO */
-static void hci_sched_sco(struct hci_dev *hdev)
-{
- struct hci_conn *conn;
- struct sk_buff *skb;
- int quote;
-
- BT_DBG("%s", hdev->name);
-
- if (!hci_conn_num(hdev, SCO_LINK))
- return;
-
- while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
- BT_DBG("skb %p len %d", skb, skb->len);
- hci_send_frame(hdev, skb);
-
- conn->sent++;
- if (conn->sent == ~0)
- conn->sent = 0;
- }
- }
-}
-
-static void hci_sched_esco(struct hci_dev *hdev)
-{
- struct hci_conn *conn;
- struct sk_buff *skb;
- int quote;
-
- BT_DBG("%s", hdev->name);
-
- if (!hci_conn_num(hdev, ESCO_LINK))
- return;
-
- while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
- &quote))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
- BT_DBG("skb %p len %d", skb, skb->len);
- hci_send_frame(hdev, skb);
-
- conn->sent++;
- if (conn->sent == ~0)
- conn->sent = 0;
- }
- }
-}
-
static void hci_sched_le(struct hci_dev *hdev)
{
struct hci_chan *chan;
@@ -4437,6 +4450,10 @@ static void hci_sched_le(struct hci_dev *hdev)
cnt--;
chan->sent++;
chan->conn->sent++;
+
+ /* Send pending SCO packets right away */
+ hci_sched_sco(hdev);
+ hci_sched_esco(hdev);
}
}
@@ -4459,9 +4476,9 @@ static void hci_tx_work(struct work_struct *work)
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
/* Schedule queues and send stuff to HCI driver */
- hci_sched_acl(hdev);
hci_sched_sco(hdev);
hci_sched_esco(hdev);
+ hci_sched_acl(hdev);
hci_sched_le(hdev);
}
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 6b1314c738b8..5e8af2658e44 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -1075,6 +1075,50 @@ DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops,
auth_payload_timeout_get,
auth_payload_timeout_set, "%llu\n");
+static ssize_t force_no_mitm_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM) ? 'Y' : 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_no_mitm_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf) - 1));
+ bool enable;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM))
+ return -EALREADY;
+
+ hci_dev_change_flag(hdev, HCI_FORCE_NO_MITM);
+
+ return count;
+}
+
+static const struct file_operations force_no_mitm_fops = {
+ .open = simple_open,
+ .read = force_no_mitm_read,
+ .write = force_no_mitm_write,
+ .llseek = default_llseek,
+};
+
DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
HCI_QUIRK_STRICT_DUPLICATE_FILTER);
DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
@@ -1134,6 +1178,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev)
&max_key_size_fops);
debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
&auth_payload_timeout_fops);
+ debugfs_create_file("force_no_mitm", 0644, hdev->debugfs, hdev,
+ &force_no_mitm_fops);
debugfs_create_file("quirk_strict_duplicate_filter", 0644,
hdev->debugfs, hdev,
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 0a591be8b0ae..966fc543c01d 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -35,6 +35,7 @@
#include "a2mp.h"
#include "amp.h"
#include "smp.h"
+#include "msft.h"
#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -746,6 +747,23 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
bacpy(&hdev->setup_addr, &rp->bdaddr);
}
+static void hci_cc_read_local_pairing_opts(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_local_pairing_opts *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+ hci_dev_test_flag(hdev, HCI_CONFIG)) {
+ hdev->pairing_opts = rp->pairing_opts;
+ hdev->max_enc_key_size = rp->max_key_size;
+ }
+}
+
static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -2607,8 +2625,16 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (ev->status) {
hci_connect_cfm(conn, ev->status);
hci_conn_del(conn);
- } else if (ev->link_type != ACL_LINK)
+ } else if (ev->link_type == SCO_LINK) {
+ switch (conn->setting & SCO_AIRMODE_MASK) {
+ case SCO_AIRMODE_CVSD:
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+ break;
+ }
+
hci_connect_cfm(conn, ev->status);
+ }
unlock:
hci_dev_unlock(hdev);
@@ -3334,6 +3360,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
hci_cc_read_bd_addr(hdev, skb);
break;
+ case HCI_OP_READ_LOCAL_PAIRING_OPTS:
+ hci_cc_read_local_pairing_opts(hdev, skb);
+ break;
+
case HCI_OP_READ_PAGE_SCAN_ACTIVITY:
hci_cc_read_page_scan_activity(hdev, skb);
break;
@@ -4307,6 +4337,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
break;
}
+ bt_dev_dbg(hdev, "SCO connected with air mode: %02x", ev->air_mode);
+
+ switch (conn->setting & SCO_AIRMODE_MASK) {
+ case SCO_AIRMODE_CVSD:
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+ break;
+ case SCO_AIRMODE_TRANSP:
+ if (hdev->notify)
+ hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_TRANSP);
+ break;
+ }
+
hci_connect_cfm(conn, ev->status);
if (ev->status)
hci_conn_del(conn);
@@ -5269,7 +5312,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
* are triggering a connection attempt. This means
- * that incoming connectioms from slave device are
+ * that incoming connections from slave device are
* accepted and also outgoing connections to slave
* devices are established when found.
*/
@@ -5353,7 +5396,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
/* Adjust for actual length */
if (len != real_len) {
- bt_dev_err_ratelimited(hdev, "advertising data len corrected");
+ bt_dev_err_ratelimited(hdev, "advertising data len corrected %u -> %u",
+ len, real_len);
len = real_len;
}
@@ -6145,6 +6189,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_num_comp_blocks_evt(hdev, skb);
break;
+ case HCI_EV_VENDOR:
+ msft_vendor_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%2.2x", hdev->name, event);
break;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 649e1e5ed446..9ea40106ef17 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -2723,6 +2723,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
uint16_t interval = opt;
struct hci_dev *hdev = req->hdev;
u8 own_addr_type;
+ /* White list is not used for discovery */
+ u8 filter_policy = 0x00;
int err;
BT_DBG("%s", hdev->name);
@@ -2744,7 +2746,7 @@ static int active_scan(struct hci_request *req, unsigned long opt)
own_addr_type = ADDR_LE_DEV_PUBLIC;
hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN,
- own_addr_type, 0);
+ own_addr_type, filter_policy);
return 0;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 117ba20ea194..1cea42ee1e92 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
return sizeof(struct sockaddr_l2);
}
+static int l2cap_get_mode(struct l2cap_chan *chan)
+{
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+ return BT_MODE_BASIC;
+ case L2CAP_MODE_ERTM:
+ return BT_MODE_ERTM;
+ case L2CAP_MODE_STREAMING:
+ return BT_MODE_STREAMING;
+ case L2CAP_MODE_LE_FLOWCTL:
+ return BT_MODE_LE_FLOWCTL;
+ case L2CAP_MODE_EXT_FLOWCTL:
+ return BT_MODE_EXT_FLOWCTL;
+ }
+
+ return -EINVAL;
+}
+
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
char __user *optval, int __user *optlen)
{
@@ -424,6 +442,20 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
break;
}
+ /* Only BR/EDR modes are supported here */
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0)
+ break;
+
memset(&opts, 0, sizeof(opts));
opts.imtu = chan->imtu;
opts.omtu = chan->omtu;
@@ -508,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
struct bt_security sec;
struct bt_power pwr;
u32 phys;
- int len, err = 0;
+ int len, mode, err = 0;
BT_DBG("sk %p", sk);
@@ -624,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;
+ case BT_MODE:
+ if (!enable_ecred) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ err = -EINVAL;
+ break;
+ }
+
+ mode = l2cap_get_mode(chan);
+ if (mode < 0) {
+ err = mode;
+ break;
+ }
+
+ if (put_user(mode, (u8 __user *) optval))
+ err = -EFAULT;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -698,10 +751,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break;
}
- chan->mode = opts.mode;
- switch (chan->mode) {
- case L2CAP_MODE_LE_FLOWCTL:
- break;
+ /* Only BR/EDR modes are supported here */
+ switch (opts.mode) {
case L2CAP_MODE_BASIC:
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
break;
@@ -715,6 +766,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
break;
}
+ if (err < 0)
+ break;
+
+ chan->mode = opts.mode;
+
BT_DBG("mode 0x%2.2x", chan->mode);
chan->imtu = opts.imtu;
@@ -763,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
return err;
}
+static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode)
+{
+ switch (mode) {
+ case BT_MODE_BASIC:
+ if (bdaddr_type_is_le(chan->src_type))
+ return -EINVAL;
+ mode = L2CAP_MODE_BASIC;
+ clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
+ break;
+ case BT_MODE_ERTM:
+ if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
+ return -EINVAL;
+ mode = L2CAP_MODE_ERTM;
+ break;
+ case BT_MODE_STREAMING:
+ if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
+ return -EINVAL;
+ mode = L2CAP_MODE_STREAMING;
+ break;
+ case BT_MODE_LE_FLOWCTL:
+ if (!bdaddr_type_is_le(chan->src_type))
+ return -EINVAL;
+ mode = L2CAP_MODE_LE_FLOWCTL;
+ break;
+ case BT_MODE_EXT_FLOWCTL:
+ /* TODO: Add support for ECRED PDUs to BR/EDR */
+ if (!bdaddr_type_is_le(chan->src_type))
+ return -EINVAL;
+ mode = L2CAP_MODE_EXT_FLOWCTL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chan->mode = mode;
+
+ return 0;
+}
+
static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
@@ -968,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
+ case BT_MODE:
+ if (!enable_ecred) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ BT_DBG("sk->sk_state %u", sk->sk_state);
+
+ if (sk->sk_state != BT_BOUND) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (get_user(opt, (u8 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ BT_DBG("opt %u", opt);
+
+ err = l2cap_set_mode(chan, opt);
+ if (err)
+ break;
+
+ BT_DBG("mode 0x%2.2x", chan->mode);
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 6552003a170e..f8c0a4fc8090 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -38,7 +38,7 @@
#include "mgmt_util.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 16
+#define MGMT_REVISION 17
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_APPEARANCE,
MGMT_OP_SET_BLOCKED_KEYS,
MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_OP_READ_SECURITY_INFO,
};
static const u16 mgmt_events[] = {
@@ -155,6 +156,7 @@ static const u16 mgmt_untrusted_commands[] = {
MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_READ_EXT_INDEX_LIST,
MGMT_OP_READ_EXT_INFO,
+ MGMT_OP_READ_SECURITY_INFO,
};
static const u16 mgmt_untrusted_events[] = {
@@ -3659,6 +3661,55 @@ unlock:
return err;
}
+static int read_security_info(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
+{
+ char buf[16];
+ struct mgmt_rp_read_security_info *rp = (void *)buf;
+ u16 sec_len = 0;
+ u8 flags = 0;
+
+ bt_dev_dbg(hdev, "sock %p", sk);
+
+ memset(&buf, 0, sizeof(buf));
+
+ hci_dev_lock(hdev);
+
+ /* When the Read Simple Pairing Options command is supported, then
+ * the remote public key validation is supported.
+ */
+ if (hdev->commands[41] & 0x08)
+ flags |= 0x01; /* Remote public key validation (BR/EDR) */
+
+ flags |= 0x02; /* Remote public key validation (LE) */
+
+ /* When the Read Encryption Key Size command is supported, then the
+ * encryption key size is enforced.
+ */
+ if (hdev->commands[20] & 0x10)
+ flags |= 0x04; /* Encryption key size enforcement (BR/EDR) */
+
+ flags |= 0x08; /* Encryption key size enforcement (LE) */
+
+ sec_len = eir_append_data(rp->sec, sec_len, 0x01, &flags, 1);
+
+ /* When the Read Simple Pairing Options command is supported, then
+ * also max encryption key size information is provided.
+ */
+ if (hdev->commands[41] & 0x08)
+ sec_len = eir_append_le16(rp->sec, sec_len, 0x02,
+ hdev->max_enc_key_size);
+
+ sec_len = eir_append_le16(rp->sec, sec_len, 0x03, SMP_MAX_ENC_KEY_SIZE);
+
+ rp->sec_len = cpu_to_le16(sec_len);
+
+ hci_dev_unlock(hdev);
+
+ return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_SECURITY_INFO, 0,
+ rp, sizeof(*rp) + sec_len);
+}
+
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@@ -7099,6 +7150,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ set_wideband_speech, MGMT_SETTING_SIZE },
+ { read_security_info, MGMT_READ_SECURITY_INFO_SIZE,
+ HCI_MGMT_UNTRUSTED },
};
void mgmt_index_added(struct hci_dev *hdev)
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
new file mode 100644
index 000000000000..d6c4e6b5ae77
--- /dev/null
+++ b/net/bluetooth/msft.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Google Corporation
+ */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "msft.h"
+
+#define MSFT_OP_READ_SUPPORTED_FEATURES 0x00
+struct msft_cp_read_supported_features {
+ __u8 sub_opcode;
+} __packed;
+struct msft_rp_read_supported_features {
+ __u8 status;
+ __u8 sub_opcode;
+ __le64 features;
+ __u8 evt_prefix_len;
+ __u8 evt_prefix[0];
+} __packed;
+
+struct msft_data {
+ __u64 features;
+ __u8 evt_prefix_len;
+ __u8 *evt_prefix;
+};
+
+static bool read_supported_features(struct hci_dev *hdev,
+ struct msft_data *msft)
+{
+ struct msft_cp_read_supported_features cp;
+ struct msft_rp_read_supported_features *rp;
+ struct sk_buff *skb;
+
+ cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES;
+
+ skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)",
+ PTR_ERR(skb));
+ return false;
+ }
+
+ if (skb->len < sizeof(*rp)) {
+ bt_dev_err(hdev, "MSFT supported features length mismatch");
+ goto failed;
+ }
+
+ rp = (struct msft_rp_read_supported_features *)skb->data;
+
+ if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
+ goto failed;
+
+ if (rp->evt_prefix_len > 0) {
+ msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
+ GFP_KERNEL);
+ if (!msft->evt_prefix)
+ goto failed;
+ }
+
+ msft->evt_prefix_len = rp->evt_prefix_len;
+ msft->features = __le64_to_cpu(rp->features);
+
+ kfree_skb(skb);
+ return true;
+
+failed:
+ kfree_skb(skb);
+ return false;
+}
+
+void msft_do_open(struct hci_dev *hdev)
+{
+ struct msft_data *msft;
+
+ if (hdev->msft_opcode == HCI_OP_NOP)
+ return;
+
+ bt_dev_dbg(hdev, "Initialize MSFT extension");
+
+ msft = kzalloc(sizeof(*msft), GFP_KERNEL);
+ if (!msft)
+ return;
+
+ if (!read_supported_features(hdev, msft)) {
+ kfree(msft);
+ return;
+ }
+
+ hdev->msft_data = msft;
+}
+
+void msft_do_close(struct hci_dev *hdev)
+{
+ struct msft_data *msft = hdev->msft_data;
+
+ if (!msft)
+ return;
+
+ bt_dev_dbg(hdev, "Cleanup of MSFT extension");
+
+ hdev->msft_data = NULL;
+
+ kfree(msft->evt_prefix);
+ kfree(msft);
+}
+
+void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct msft_data *msft = hdev->msft_data;
+ u8 event;
+
+ if (!msft)
+ return;
+
+ /* When the extension has defined an event prefix, check that it
+ * matches, and otherwise just return.
+ */
+ if (msft->evt_prefix_len > 0) {
+ if (skb->len < msft->evt_prefix_len)
+ return;
+
+ if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
+ return;
+
+ skb_pull(skb, msft->evt_prefix_len);
+ }
+
+ /* Every event starts at least with an event code and the rest of
+ * the data is variable and depends on the event code.
+ */
+ if (skb->len < 1)
+ return;
+
+ event = *skb->data;
+ skb_pull(skb, 1);
+
+ bt_dev_dbg(hdev, "MSFT vendor event %u", event);
+}
diff --git a/net/bluetooth/msft.h b/net/bluetooth/msft.h
new file mode 100644
index 000000000000..5aa9130e1f8a
--- /dev/null
+++ b/net/bluetooth/msft.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Google Corporation
+ */
+
+#if IS_ENABLED(CONFIG_BT_MSFTEXT)
+
+void msft_do_open(struct hci_dev *hdev);
+void msft_do_close(struct hci_dev *hdev);
+void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb);
+
+#else
+
+static inline void msft_do_open(struct hci_dev *hdev) {}
+static inline void msft_do_close(struct hci_dev *hdev) {}
+static inline void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) {}
+
+#endif
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 1476a91ce935..df22cbf94693 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -854,7 +854,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
u32 passkey = 0;
- int ret = 0;
+ int ret;
/* Initialize key for JUST WORKS */
memset(smp->tk, 0, sizeof(smp->tk));
@@ -883,9 +883,16 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
smp->method = JUST_WORKS;
- /* If Just Works, Continue with Zero TK */
+ /* If Just Works, Continue with Zero TK and ask user-space for
+ * confirmation */
if (smp->method == JUST_WORKS) {
- set_bit(SMP_FLAG_TK_VALID, &smp->flags);
+ ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
+ hcon->type,
+ hcon->dst_type,
+ passkey, 1);
+ if (ret)
+ return ret;
+ set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
return 0;
}
@@ -2194,7 +2201,7 @@ mackey_and_ltk:
if (err)
return SMP_UNSPECIFIED;
- if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+ if (smp->method == REQ_OOB) {
if (hcon->out) {
sc_dhkey_check(smp);
SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
@@ -2209,6 +2216,9 @@ mackey_and_ltk:
confirm_hint = 0;
confirm:
+ if (smp->method == JUST_WORKS)
+ confirm_hint = 1;
+
err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
hcon->dst_type, passkey, confirm_hint);
if (err)
@@ -2385,12 +2395,17 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
authreq |= SMP_AUTH_CT2;
}
- /* Require MITM if IO Capability allows or the security level
- * requires it.
+ /* Don't attempt to set MITM if setting is overridden by debugfs
+ * Needed to pass certification test SM/MAS/PKE/BV-01-C
*/
- if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
- hcon->pending_sec_level > BT_SECURITY_MEDIUM)
- authreq |= SMP_AUTH_MITM;
+ if (!hci_dev_test_flag(hcon->hdev, HCI_FORCE_NO_MITM)) {
+ /* Require MITM if IO Capability allows or the security level
+ * requires it.
+ */
+ if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
+ hcon->pending_sec_level > BT_SECURITY_MEDIUM)
+ authreq |= SMP_AUTH_MITM;
+ }
if (hcon->role == HCI_ROLE_MASTER) {
struct smp_cmd_pairing cp;