From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C52241226E for ; Fri, 3 Jul 2026 10:35:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783074940; cv=none; b=pd+BtS9s5WO8U+cOWttD4ee2DyD6+7hIFlQAcpBYMzHgs5rJbgogNAJvy85EdYdvSb0TScoqSLk1MWcPtq034IWin3whRmjH2WfUlFdXS2Q2iGw/a3802Pc5s4dSkrHuLDUUGx0A1NrfBxFxqooanybWizt25ufw5RJSWX53Iz4= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783074940; c=relaxed/simple; bh=SXGWBGyC4ztW/hWfVZcMWMzHuitSUoGr76VgPee7BM8=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type: Content-Type; b=pLt4gKJZOP+KmpV8+jRi10UKTNFAdTzUro6xj9IB2bdXF98SsK/lc8YbfxOI7h6yjGdACTg70AWwaQfR8T00wjWxsDEKQJ5bz43Z5THH+OMaNUbacxQdG4i91oesWOhhNlbWMx4nA/Bn/2J8OgF2YjHS6cZglMQB1vteJsuoLWg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=XpcgDUM/; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="XpcgDUM/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1783074930; bh=SXGWBGyC4ztW/hWfVZcMWMzHuitSUoGr76VgPee7BM8=; h=From:To:Subject:Date:From; b=XpcgDUM/kBJwUbNiSsYKZDXCS1XA6ro/UEjswRDYWKjsC598p2Yw2YKQlICex2xz9 3p7kCtvPSJ+1cornLTaYwp8T/Z55JxF7TF6D4YacmmybgAqfp/ijNG4l5uboKLJfgL G+Ol968od+Lj6s4zOdHb3DJ5dWKRewnZZyTvqFNT35cp6JVLfmQzUl+I4V4WiMmz3s Howt9X8KMoG3HcoWIMTyYLIrlsShulAq1RCFsbsfL2bZ0VTr/PsCiwDzOXMZkHw1nM xI9s4xCEUgdlYzKpmOSOml6almdOzGasPsZ/P/4Gk9fV24FdFjgH5FGb/I4ozD28yA jz+seu8OV1KEQ== Received: from fdanis-ThinkPad-X1.. (unknown [100.64.1.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: fdanis) by bali.collaboradmins.com (Postfix) with ESMTPSA id 842A917E04D4 for ; Fri, 3 Jul 2026 12:35:30 +0200 (CEST) From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?= To: linux-bluetooth@vger.kernel.org Subject: [PATCH v3] Bluetooth: MGMT: Add management security level changed event Date: Fri, 3 Jul 2026 12:35:24 +0200 Message-ID: <20260703103524.536763-1-frederic.danis@collabora.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-bluetooth@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit 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 Signed-off-by: Frédéric Danis --- 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. include/net/bluetooth/hci_core.h | 15 +++++++++++-- include/net/bluetooth/mgmt.h | 13 ++++++++++++ net/bluetooth/hci_conn.c | 8 +++++++ net/bluetooth/hci_event.c | 20 ++++++++++++++++-- net/bluetooth/mgmt.c | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4ca09298e11a..43099102075a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -2178,6 +2178,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; @@ -2200,11 +2202,20 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status) encrypt = 0x01; if (!status) { - if (conn->sec_level == BT_SECURITY_SDP) + bool sec_level_changed = false; + + if (conn->sec_level == BT_SECURITY_SDP) { conn->sec_level = BT_SECURITY_LOW; + sec_level_changed = true; + } - if (conn->pending_sec_level > conn->sec_level) + if (conn->pending_sec_level > conn->sec_level) { conn->sec_level = conn->pending_sec_level; + sec_level_changed = true; + } + + if (sec_level_changed) + mgmt_security_level_changed(conn); } mutex_lock(&hci_cb_list_lock); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 08daed7a96d5..d9f7f2df9209 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -1192,3 +1192,16 @@ struct mgmt_ev_mesh_device_found { struct mgmt_ev_mesh_pkt_cmplt { __u8 handle; } __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 0x0033 +struct mgmt_ev_security_level_changed { + struct mgmt_addr_info addr; + __u8 tlv_data[]; +} __packed; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1966cd153d97..376bde85ce6d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1200,6 +1200,11 @@ 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(conn); + list_for_each_entry_safe(link, t, &conn->link_list, list) { struct hci_conn *child = link->conn; @@ -1502,6 +1507,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } conn->sec_level = BT_SECURITY_LOW; + mgmt_security_level_changed(conn); conn->conn_timeout = conn_timeout; conn->le_adv_phy = phy; conn->le_adv_sec_phy = sec_phy; @@ -1729,6 +1735,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst, conn->state = BT_CONNECT; set_bit(HCI_CONN_SCANNING, &conn->flags); conn->sec_level = BT_SECURITY_LOW; + mgmt_security_level_changed(conn); conn->pending_sec_level = sec_level; conn->conn_timeout = conn_timeout; conn->conn_reason = conn_reason; @@ -1777,6 +1784,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, int err; acl->sec_level = BT_SECURITY_LOW; + mgmt_security_level_changed(acl); acl->pending_sec_level = sec_level; acl->auth_type = auth_type; acl->conn_timeout = timeout; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5b867aa99332..2cbae697f361 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -765,6 +765,7 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data, status = HCI_ERROR_AUTH_FAILURE; clear_bit(HCI_CONN_ENCRYPT, &conn->flags); clear_bit(HCI_CONN_AES_CCM, &conn->flags); + mgmt_security_level_changed(conn); } /* Update the key encryption size with the connection one */ @@ -3187,6 +3188,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, } if (!status) { + bool encrypt_change = false; + status = hci_conn_set_handle(conn, __le16_to_cpu(ev->handle)); if (status) goto done; @@ -3209,8 +3212,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, if (test_bit(HCI_AUTH, &hdev->flags)) set_bit(HCI_CONN_AUTH, &conn->flags); - if (test_bit(HCI_ENCRYPT, &hdev->flags)) + if (test_bit(HCI_ENCRYPT, &hdev->flags)) { set_bit(HCI_CONN_ENCRYPT, &conn->flags); + encrypt_change = true; + } /* "Link key request" completed ahead of "connect request" completes */ if (ev->encr_mode == 1 && !test_bit(HCI_CONN_ENCRYPT, &conn->flags) && @@ -3220,11 +3225,15 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data, key = hci_find_link_key(hdev, &ev->bdaddr); if (key) { set_bit(HCI_CONN_ENCRYPT, &conn->flags); + encrypt_change = true; hci_read_enc_key_size(hdev, conn); hci_encrypt_cfm(conn, ev->status); } } + if (encrypt_change) + mgmt_security_level_changed(conn); + /* Get remote features */ if (conn->type == ACL_LINK) { struct hci_cp_read_remote_features cp; @@ -3510,6 +3519,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, void *data, clear_bit(HCI_CONN_AUTH_FAILURE, &conn->flags); set_bit(HCI_CONN_AUTH, &conn->flags); conn->sec_level = conn->pending_sec_level; + mgmt_security_level_changed(conn); } else { if (ev->status == HCI_ERROR_PIN_OR_KEY_MISSING) set_bit(HCI_CONN_AUTH_FAILURE, &conn->flags); @@ -3625,9 +3635,12 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data, if ((conn->type == ACL_LINK && ev->encrypt == 0x02) || conn->type == LE_LINK) set_bit(HCI_CONN_AES_CCM, &conn->flags); + + mgmt_security_level_changed(conn); } else { clear_bit(HCI_CONN_ENCRYPT, &conn->flags); clear_bit(HCI_CONN_AES_CCM, &conn->flags); + mgmt_security_level_changed(conn); } } @@ -5218,8 +5231,10 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, void *data, if (conn->type != LE_LINK) goto unlock; - if (!ev->status) + if (!ev->status) { conn->sec_level = conn->pending_sec_level; + mgmt_security_level_changed(conn); + } clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); @@ -5850,6 +5865,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status, mgmt_device_connected(hdev, conn, NULL, 0); conn->sec_level = BT_SECURITY_LOW; + mgmt_security_level_changed(conn); conn->state = BT_CONFIG; /* Store current advertising instance as connection advertising instance diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 733a4b70e10c..d398bf20a545 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -177,6 +177,7 @@ static const u16 mgmt_events[] = { MGMT_EV_CONTROLLER_RESUME, MGMT_EV_ADV_MONITOR_DEVICE_FOUND, MGMT_EV_ADV_MONITOR_DEVICE_LOST, + MGMT_EV_SECURITY_LEVEL_CHANGED, }; static const u16 mgmt_untrusted_commands[] = { @@ -10536,6 +10537,41 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, mgmt_adv_monitor_device_found(hdev, bdaddr, report_device, skb, NULL); } +void mgmt_security_level_changed(struct hci_conn *conn) +{ + struct { + struct mgmt_ev_security_level_changed ev; + struct { + struct mgmt_tlv_hdr hdr; + __u8 value; + } __packed level; + struct { + struct mgmt_tlv_hdr hdr; + __u8 value; + } __packed enc_type; + } __packed data; + + bacpy(&data.ev.addr.bdaddr, &conn->dst); + data.ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); + + data.level.hdr.type = cpu_to_le16(MGMT_SEC_LEVEL_CHANGED_PARAM_LEVEL); + data.level.hdr.length = sizeof(__u8); + data.level.value = conn->sec_level; + + data.enc_type.hdr.type = cpu_to_le16( + MGMT_SEC_LEVEL_CHANGED_PARAM_ENC_TYPE); + data.enc_type.hdr.length = sizeof(__u8); + if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags)) + data.enc_type.value = MGMT_CONN_SEC_ENCRYPT_NONE; + else if (test_bit(HCI_CONN_AES_CCM, &conn->flags)) + data.enc_type.value = MGMT_CONN_SEC_ENCRYPT_AES_CCM; + else + data.enc_type.value = MGMT_CONN_SEC_ENCRYPT_E0; + + mgmt_event(MGMT_EV_SECURITY_LEVEL_CHANGED, conn->hdev, &data, + sizeof(data), NULL); +} + void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len) { -- 2.43.0