Skip to contentSkip to conversation

Bluetooth: MGMT: Add management security level changed event

PATCH Superseded Frédéric Danis 1 participant 1 message Oct 1

Subsystems (2)

v8 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

PATCH v8 1/1 Bluetooth: MGMT: Add management security level changed event

Add an event on device security level or encryption type change to let user space know which level is currently in use. Reset security level to 0 on disconnection so further connections will correctly report security level changes.

This will be used for BlueZ qualification automation.

Assisted-by GPT:GPT-5.3-Codex
Assisted-by Copilot:claude-sonnet-5

---
v1->v2:
- Add encryption type to the mgmt event
- Send event on security level or encryption type change
v2->v3:
- Replace fixed security level and ancryption type parameters by TLV array to allow later enhancement.
v3->v4:
- Change MGMT_EV_SECURITY_LEVEL_CHANGED to 0x0034
- Add __counted_by for the tlv_data
- dynamically allow the mgmt_ev_security_level_changed structure
v4->v5: Address Sashiko comments:
- stop overwriting tlv_count and replace it by tlv_length as kzalloc_flex() should already set it to the byte length. This depends on compiler supporting __builtin_counted_by_ref(), so explicitly set it too.
- filter on link type to prevent sending the event for child connections
- remove premature calls to mgmt_security_level_changed()
- send events only on security level changes
v5->v6: Address Sashiko comments:
- avoid a duplicate MGMT event in hci_conn_complete_evt() by not setting encrypt_change when the link-key branch already reports the change via hci_encrypt_cfm()
- defer reporting for encrypted ACL links until hci_cc_read_enc_key_size() has validated the encryption key size, instead of reporting a transient state that a key-size downgrade could immediately invalidate
v6->v7: Address Sashiko comments:
- report the security level only from mgmt_device_connected(), right after MGMT_EV_DEVICE_CONNECTED, when the connection is still being set up (BT_CONFIG) at the time the change happens, so userspace is never told about a security level before knowing about the connection
- in hci_cc_read_enc_key_size(), always pre-apply hci_encrypt_cfm()'s upgrade rules, since hci_encrypt_change_evt() can already have set conn->sec_level directly and defeat hci_encrypt_cfm()'s own change detection
- in hci_cc_read_enc_key_size(), use test_bit(HCI_CONN_MGMT_CONNECTED, ...) instead of
conn->state == BT_CONFIG to decide whether to report immediately or defer to mgmt_device_connected(): the latter can already have fired while still in BT_CONFIG (e.g. pairing with authentication pending), which was silently dropping the encryption type update
- keep reporting the security level reset in hci_conn_unlink() on disconnection, after MGMT_EV_DEVICE_DISCONNECTED: userspace (e.g. BlueZ) relies on this event to clear its cached security level and encryption type
- in le_conn_complete_evt(), set conn->sec_level to BT_SECURITY_LOW before calling mgmt_device_connected() instead of after, to avoid reporting a spurious initial SDP event before the real one
v7->v8: Address Sashiko comments:
- move the HCI_CONN_MGMT_CONNECTED check into mgmt_security_level_changed() itself, dropping the now redundant check from all call sites, to stop premature events for connections not yet announced to userspace
- change mgmt_connect_failed() to peek at HCI_CONN_MGMT_CONNECTED instead of consuming it, so hci_conn_unlink() can still see it to report the security level reset
- remove the now dead mgmt_security_level_changed() call in hci_conn_complete_evt()
- stop reporting the security level right after authentication completes in hci_auth_complete_evt(), before encryption is actually confirmed
- downgrade conn->sec_level to BT_SECURITY_LOW before reporting when encryption is disabled (hci_encrypt_change_evt()) or the key-size check fails (hci_cc_read_enc_key_size()), instead of advertising a stale elevated level alongside MGMT_CONN_SEC_ENCRYPT_NONE
- set conn->sec_level to BT_SECURITY_LOW for incoming BR/EDR connections in hci_conn_request_evt(), like already done for LE in le_conn_complete_evt()
- skip the security level reset report in hci_conn_unlink() when hdev is down, since no MGMT_EV_DEVICE_DISCONNECTED was ever sent in that case
- remove the dead pending_sec_level > sec_level upgrade check in hci_encrypt_cfm(), unreachable since all callers already set conn->sec_level = conn->pending_sec_level themselves

 include/net/bluetooth/hci_core.h |  18 +++--
 include/net/bluetooth/mgmt.h     |  14 ++++
 net/bluetooth/hci_conn.c         |  28 ++++++++
 net/bluetooth/hci_event.c        | 110 +++++++++++++++++++++++++++++--
 net/bluetooth/mgmt.c             |  67 ++++++++++++++++++-
 5 files changed, 225 insertions(+), 12 deletions(-)
include/net/bluetooth/hci_core.h +14 -4
index fb85a0ccb250..f631d9484a0b 100644
@@ -2255,6 +2255,8 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
 		conn->security_cfm_cb(conn, status);
 }
 
+void mgmt_security_level_changed(struct hci_conn *conn);
+
 static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
 {
 	struct hci_cb *cb;
@@ -2277,11 +2279,19 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
 		encrypt = 0x01;
 
 	if (!status) {
-		if (conn->sec_level == BT_SECURITY_SDP)
+		/* Callers either set conn->sec_level = conn->pending_sec_level
+		 * unconditionally before calling this function, or never
+		 * reach here with status == 0 (e.g. conn->state == BT_CONFIG
+		 * returns early above, or status is actually an error).  So
+		 * conn->pending_sec_level is never greater than
+		 * conn->sec_level at this point; only the initial
+		 * BT_SECURITY_SDP -> BT_SECURITY_LOW bump can still happen
+		 * here.
+		 */
+		if (conn->sec_level == BT_SECURITY_SDP) {
 			conn->sec_level = BT_SECURITY_LOW;
-
-		if (conn->pending_sec_level > conn->sec_level)
-			conn->sec_level = conn->pending_sec_level;
+			mgmt_security_level_changed(conn);
+		}
 	}
 
 	mutex_lock(&hci_cb_list_lock);
include/net/bluetooth/mgmt.h +14 -0
index 1e22eab1081c..b02e915bb44c 100644
@@ -1221,3 +1221,17 @@ struct mgmt_ev_conn_subrate {
 	__le16	cont_num;
 	__le16	supv_timeout;
 } __packed;
+
+#define MGMT_CONN_SEC_ENCRYPT_NONE		0x00
+#define MGMT_CONN_SEC_ENCRYPT_E0		0x01
+#define MGMT_CONN_SEC_ENCRYPT_AES_CCM		0x02
+
+#define MGMT_SEC_LEVEL_CHANGED_PARAM_LEVEL	0x0000
+#define MGMT_SEC_LEVEL_CHANGED_PARAM_ENC_TYPE	0x0001
+
+#define MGMT_EV_SECURITY_LEVEL_CHANGED		0x0034
+struct mgmt_ev_security_level_changed {
+	struct mgmt_addr_info addr;
+	__u8	tlv_length;
+	__u8	tlv_data[] __counted_by(tlv_length);
+} __packed;
net/bluetooth/hci_conn.c +28 -0
index cf44452e0766..29da3fe2b953 100644
@@ -1233,6 +1233,29 @@ static void hci_conn_unlink(struct hci_conn *conn)
 	if (!conn->parent) {
 		struct hci_link *link, *t;
 
+		conn->sec_level = BT_SECURITY_SDP;
+		clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
+		clear_bit(HCI_CONN_AES_CCM, &conn->flags);
+		/* mgmt_security_level_changed() reports the reset only if
+		 * this connection was actually reported to userspace as
+		 * connected (it checks HCI_CONN_MGMT_CONNECTED itself), so it
+		 * must be called before the flag is cleared below.
+		 * Callers that already know whether the connection was
+		 * mgmt-connected (e.g. disconnect event handlers) only peek
+		 * at the flag, so it is still set here; this is the place
+		 * actually clearing it.
+		 *
+		 * Skip the report if hdev is down: this means
+		 * hci_dev_close_sync()/hci_conn_hash_flush() is in progress
+		 * and no MGMT_EV_DEVICE_DISCONNECTED was ever sent for this
+		 * connection, so sending a security level reset here would
+		 * be a spurious, order-less event; the adapter-wide powered
+		 * off notification is enough for userspace to reset state.
+		 */
+		if (test_bit(HCI_UP, &hdev->flags))
+			mgmt_security_level_changed(conn);
+		clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
+
 		list_for_each_entry_safe(link, t, &conn->link_list, list) {
 			struct hci_conn *child = link->conn;
 
@@ -1543,6 +1566,11 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
 		conn->pending_sec_level = sec_level;
 	}
 
+	/* Do not report the security level here: conn->dst may still hold
+	 * the peer's RPA instead of its identity address, and the connection
+	 * is not established yet. le_conn_complete_evt() reports it once the
+	 * connection completes and the identity address has been resolved.
+	 */
 	conn->sec_level = BT_SECURITY_LOW;
 	conn->conn_timeout = conn_timeout;
 	conn->le_adv_phy = phy;
net/bluetooth/hci_event.c +103 -7

183 lines not on this page · raw message

net/bluetooth/mgmt.c +66 -1

91 lines not on this page · raw message

Signature · 1 line
2.43.0