[PATCH] Bluetooth: Fix for L2CAP/LE/CFC/BV-15-C
From: Magdalena Kasenberg <hidden>
Date: 2021-02-22 10:31:42
Subsystem:
bluetooth subsystem, the rest · Maintainers:
Marcel Holtmann, Luiz Augusto von Dentz, Linus Torvalds
This is required for the qualification test L2CAP/LE/CFC/BV-15-C Implementation does not allow to set different key size for SMP and L2CAP, which is needed for a current specification of the test. This fix workarounds it with the debugfs variable le_l2cap_min_key_size. Logs from the test when the IUT uses a min and max l2cap encryption key size 16. $ echo 16 > /sys/kernel/debug/bluetooth/hci0/le_l2cap_min_key_size The lower tester uses a key size 7.
ACL Data RX: Handle 99 flags 0x02 dlen 11 #34 [hci0] 25.007392
SMP: Pairing Request (0x01) len 6
IO capability: DisplayYesNo (0x01)
OOB data: Authentication data not present (0x00)
Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09)
Max encryption key size: 7
Initiator key distribution: <none> (0x00)
Responder key distribution: <none> (0x00)
< ACL Data TX: Handle 99 flags 0x00 dlen 11 #35 [hci0] 25.007591
SMP: Pairing Response (0x02) len 6
IO capability: KeyboardDisplay (0x04)
OOB data: Authentication data not present (0x00)
Authentication requirement: Bonding, No MITM, SC, No Keypresses (0x09)
Max encryption key size: 16
Initiator key distribution: <none> (0x00)
Responder key distribution: <none> (0x00)
@ MGMT Event: New Long Term Key (0x000a) plen 37 {0x0001} [hci0] 28.788872
Store hint: Yes (0x01)
LE Address: C0:DE:C0:FF:FF:01 (OUI C0-DE-C0)
Key type: Unauthenticated key from P-256 (0x02)
Master: 0x00
Encryption size: 7
Diversifier: 0000
Randomizer: 0000000000000000
Key: 529e11e8c7b9f5000000000000000000
<snip>
After pairing with key size 7, L2CAP connection is requested which
requires key size 16.
ACL Data RX: Handle 99 flags 0x02 dlen 18 #56 [hci0] 34.998084
LE L2CAP: LE Connection Request (0x14) ident 3 len 10
PSM: 244 (0x00f4)
Source CID: 64
MTU: 256
MPS: 284
Credits: 1
< ACL Data TX: Handle 99 flags 0x00 dlen 18 #57 [hci0] 34.998325
LE L2CAP: LE Connection Response (0x15) ident 3 len 10
Destination CID: 0
MTU: 0
MPS: 0
Credits: 0
Result: Connection refused - insufficient encryption key size (0x0007)
Signed-off-by: Magdalena Kasenberg <redacted>
Reviewed-by: Szymon Janc <redacted>
Cc: Szymon Janc <redacted>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_core.c | 1 +
net/bluetooth/hci_debugfs.c | 30 ++++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 25 +++++++++++++++++++++++++
4 files changed, 57 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index ebdd4afe30d2..0bf0543efec5 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h@@ -379,6 +379,7 @@ struct hci_dev { __u16 auth_payload_timeout; __u8 min_enc_key_size; __u8 max_enc_key_size; + __u8 le_l2cap_min_key_size; __u8 pairing_opts; __u8 ssp_debug_mode; __u8 hw_error_code;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b0d9c36acc03..9ef4b39b380c 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c@@ -3788,6 +3788,7 @@ struct hci_dev *hci_alloc_dev(void) hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE; hdev->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT; hdev->min_enc_key_size = HCI_MIN_ENC_KEY_SIZE; + hdev->le_l2cap_min_key_size = HCI_MIN_ENC_KEY_SIZE; /* default 1.28 sec page scan */ hdev->def_page_scan_type = PAGE_SCAN_TYPE_STANDARD;
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 1a0ab58bfad0..dec8b96b8427 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c@@ -1096,6 +1096,34 @@ static int max_key_size_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(max_key_size_fops, max_key_size_get, max_key_size_set, "%llu\n"); +static int le_l2cap_min_key_size_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val > SMP_MAX_ENC_KEY_SIZE || val < SMP_MIN_ENC_KEY_SIZE) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_l2cap_min_key_size = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int le_l2cap_min_key_size_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_l2cap_min_key_size; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(le_l2cap_min_key_size_fops, le_l2cap_min_key_size_get, + le_l2cap_min_key_size_set, "%llu\n"); + static int auth_payload_timeout_set(void *data, u64 val) { struct hci_dev *hdev = data;
@@ -1226,6 +1254,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev) &min_key_size_fops); debugfs_create_file("max_key_size", 0644, hdev->debugfs, hdev, &max_key_size_fops); + debugfs_create_file("le_l2cap_min_key_size", 0644, hdev->debugfs, hdev, + &le_l2cap_min_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,
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 72c2f5226d67..d9a3a1c1f366 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c@@ -5742,6 +5742,20 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, return err; } +static bool le_l2cap_key_size_sufficient(struct hci_conn *hcon, u8 sec_level) +{ + struct smp_ltk *ltk; + + if (sec_level == BT_SECURITY_LOW) + return true; + + ltk = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role); + if (ltk && ltk->enc_size >= hcon->hdev->le_l2cap_min_key_size) + return true; + + return false; +} + static int l2cap_le_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
@@ -5788,6 +5802,12 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, goto response_unlock; } + if (!le_l2cap_key_size_sufficient(conn->hcon, pchan->sec_level)) { + result = L2CAP_CR_LE_BAD_KEY_SIZE; + chan = NULL; + goto response_unlock; + } + /* Check for valid dynamic CID range */ if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) { result = L2CAP_CR_LE_INVALID_SCID;
@@ -5969,6 +5989,11 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, goto unlock; } + if (!le_l2cap_key_size_sufficient(conn->hcon, pchan->sec_level)) { + result = L2CAP_CR_LE_BAD_KEY_SIZE; + goto unlock; + } + result = L2CAP_CR_LE_SUCCESS; cmd_len -= sizeof(*req); num_scid = cmd_len / sizeof(u16);
--
2.25.1