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 <magdalena.kasenberg@xxxxxxxxxxx> Reviewed-by: Szymon Janc <szymon.janc@xxxxxxxxxxx> Cc: Szymon Janc <szymon.janc@xxxxxxxxxxx> --- 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