]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: add support for encryption/decryption of (Re)Association frames
authorKavita Kavita <kavita.kavita@oss.qualcomm.com>
Wed, 14 Jan 2026 11:18:59 +0000 (16:48 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 14 Jan 2026 13:55:38 +0000 (14:55 +0100)
Currently, mac80211 does not encrypt or decrypt (Re)Association frames
(Request and Response) because temporal keys are not yet available at
that stage.

With extensions from IEEE P802.11bi, e.g. EPPKE, temporal keys can be
established before association. This enables the encryption and
decryption of (Re)Association Request/Response frames.

Add support to unset the IEEE80211_TX_INTFL_DONT_ENCRYPT flag when
the peer is marked as an Enhanced Privacy Protection (EPP) peer and
encryption keys are available for the connection in non-AP STA mode,
allowing secure transmission of (Re)Association Request frames.

Drop unprotected (Re)Association Request/Response frames received from
an EPP peer.

Co-developed-by: Sai Pratyusha Magam <quic_smagam@quicinc.com>
Signed-off-by: Sai Pratyusha Magam <quic_smagam@quicinc.com>
Signed-off-by: Kavita Kavita <kavita.kavita@oss.qualcomm.com>
Link: https://patch.msgid.link/20260114111900.2196941-9-kavita.kavita@oss.qualcomm.com
[remove useless parentheses]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/tx.c
net/mac80211/wpa.c

index 0a8875e0709b281dc0f77d1e16b6e01e0a9cd9a5..dc757cb329740d621f6a9deb4e9ffe258e1b7d67 100644 (file)
@@ -2393,6 +2393,14 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb, int tid, int link_id,
                                 enum nl80211_band band);
 
+static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
+                                                    struct sta_info *sta)
+{
+       return (sta && sta->sta.epp_peer &&
+               (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) ||
+                ieee80211_is_assoc_resp(fc) || ieee80211_is_reassoc_resp(fc)));
+}
+
 /* sta_out needs to be checked for ERR_PTR() before using */
 int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
                            struct sk_buff *skb,
index 0c31a0602ea97c4845d7cb93d6a9c19ecf9c9972..6e468c4fcda2764dcc8589c9fd2d446587a28d22 100644 (file)
@@ -2155,6 +2155,8 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_prep_tx_info info = {};
        unsigned int link_id, n_links = 0;
        u16 present_elems[PRESENT_ELEMS_MAX] = {};
+       struct sta_info *sta;
+       bool assoc_encrypt;
        void *capab_pos;
        size_t size;
        int ret;
@@ -2335,7 +2337,15 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        info.link_id = assoc_data->assoc_link_id;
        drv_mgd_prepare_tx(local, sdata, &info);
 
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       sta = sta_info_get_bss(sdata, sdata->vif.cfg.ap_addr);
+
+       assoc_encrypt = sta && sta->sta.epp_peer &&
+                       wiphy_dereference(sdata->local->hw.wiphy,
+                                         sta->ptk[sta->ptk_idx]);
+
+       if (!assoc_encrypt)
+               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
        if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
                                                IEEE80211_TX_INTFL_MLME_CONN_TX;
index e0ccd97498536da6c5276fb09d798229c1e7090b..9a2b0ef2f21a8da5c8a469301a29cdfd9b2740d4 100644 (file)
@@ -2609,6 +2609,14 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
            (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))
                return RX_DROP_U_UNPROT_ROBUST_ACTION;
 
+       /*
+        * Drop unprotected (Re)Association Request/Response frame received from
+        * an EPP Peer.
+        */
+       if (!ieee80211_has_protected(fc) &&
+           ieee80211_require_encrypted_assoc(fc, rx->sta))
+               return RX_DROP_U_UNPROT_UCAST_MGMT;
+
        return RX_CONTINUE;
 }
 EXPORT_SYMBOL_IF_MAC80211_KUNIT(ieee80211_drop_unencrypted_mgmt);
index 1b55e83404135abf6b0e589a9519ac219f0d8904..007f5a368d41400e38f97a46baad42bf7d14e826 100644 (file)
@@ -640,7 +640,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                        if (!ieee80211_is_data_present(hdr->frame_control) &&
                            !ieee80211_use_mfp(hdr->frame_control, tx->sta,
                                               tx->skb) &&
-                           !ieee80211_is_group_privacy_action(tx->skb))
+                           !ieee80211_is_group_privacy_action(tx->skb) &&
+                           !ieee80211_require_encrypted_assoc(hdr->frame_control,
+                                                              tx->sta))
                                tx->key = NULL;
                        else
                                skip_hw = (tx->key->conf.flags &
index 4a858112e4ef94f2160a00e8c34e3cd9c0c9690f..fdf98c21d32c0bef54f66c6704b8c973ab14b52b 100644 (file)
@@ -527,7 +527,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
        if (!ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_robust_mgmt_frame(skb))
+           !ieee80211_is_robust_mgmt_frame(skb) &&
+           !ieee80211_require_encrypted_assoc(hdr->frame_control, rx->sta))
                return RX_CONTINUE;
 
        if (status->flag & RX_FLAG_DECRYPTED) {
@@ -723,7 +724,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
        if (!ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_robust_mgmt_frame(skb))
+           !ieee80211_is_robust_mgmt_frame(skb) &&
+           !ieee80211_require_encrypted_assoc(hdr->frame_control, rx->sta))
                return RX_CONTINUE;
 
        if (status->flag & RX_FLAG_DECRYPTED) {