wifi: cfg80211: clarify and tighten key checks

Currently, we accept per-STA GTK for any interface type
if the IBSS_RSN flag is set, which doesn't make sense,
and also accept various key indices that aren't really
(meant to be) supported, such as IGTK/BIGTK on IBSS or
AP_VLAN etc.

For MESH and NAN_DATA interface types, per-STA GTKs are
required, so their support shouldn't depend on IBSS_RSN.

Conversely a driver setting IBSS_RSN doesn't really say
it also accepts per-STA GTK for other interface types.

Move more checks into cfg80211_valid_key_idx() and make
them more precise:
 - allow IGTK and, if supported, BIGTK for NAN
 - allow per-STA (RX) GTK only for
   - NAN_DATA
   - IBSS if IBSS_RSN is supported
   - MESH
 - allow B/I/GTK for station/P2P-client without mac_addr
   for RX with the current AP (historic API quirk),
   subject to support
 - allow TX GTK for AP/P2P-GO/AP_VLAN
 - allow TX IGTK/BIGTK for AP/P2P-GO subject to support

Other settings are rejected, clearing up corner cases
and disallowing unexpected settings.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260715212403.725e6b63e890.I24684374112bb94d0633d61ef76ecb8a1517f7f1@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-07-15 21:24:40 +03:00
parent 3002812cbe
commit 8ff047b9c7
4 changed files with 87 additions and 39 deletions

View File

@ -443,8 +443,9 @@ void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
int key_idx, bool pairwise);
bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
int key_idx, bool pairwise,
const u8 *mac_addr);
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct key_params *params, int key_idx,

View File

@ -5409,7 +5409,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->get_key)
return -EOPNOTSUPP;
if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
return -ENOENT;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@ -5663,8 +5663,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
key.type != NL80211_KEYTYPE_GROUP)
return -EINVAL;
if (!cfg80211_valid_key_idx(rdev, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE))
if (!cfg80211_valid_key_idx(wdev, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr))
return -EINVAL;
if (!rdev->ops->del_key)
@ -5672,10 +5673,6 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
err = nl80211_key_allowed(wdev);
if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
if (!err)
err = nl80211_validate_key_link_id(info, wdev, link_id,
key.type == NL80211_KEYTYPE_PAIRWISE);

View File

@ -241,10 +241,8 @@ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher)
return false;
}
static bool
cfg80211_igtk_cipher_supported(struct cfg80211_registered_device *rdev)
static bool cfg80211_igtk_cipher_supported(struct wiphy *wiphy)
{
struct wiphy *wiphy = &rdev->wiphy;
int i;
for (i = 0; i < wiphy->n_cipher_suites; i++) {
@ -260,27 +258,86 @@ cfg80211_igtk_cipher_supported(struct cfg80211_registered_device *rdev)
return false;
}
bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
int key_idx, bool pairwise)
bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
int key_idx, bool pairwise,
const u8 *mac_addr)
{
int max_key_idx;
if (pairwise)
max_key_idx = 3;
else if (wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION) ||
wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
max_key_idx = 7;
else if (cfg80211_igtk_cipher_supported(rdev))
max_key_idx = 5;
else
max_key_idx = 3;
if (key_idx < 0 || key_idx > max_key_idx)
if (WARN_ON(!wdev))
return false;
return true;
if (key_idx < 0)
return false;
/*
* Can't differentiate ciphers here so allow 0..3.
* Pairwise keys must be for a station (MAC address given).
*/
if (pairwise) {
if (!mac_addr)
return false;
return key_idx < 4;
}
/*
* For group keys, mac_addr==NULL means setting a group key
* for TX, which is only supported on some interface types,
* except for STATION/P2P_CLIENT, where it's setting the RX
* key with the current AP (for legacy reasons.)
*
* Apart from that exception, a non-NULL mac_addr means RX
* key being set.
*/
switch (wdev->iftype) {
case NL80211_IFTYPE_ADHOC:
if (!(wdev->wiphy->flags & WIPHY_FLAG_IBSS_RSN))
return false;
fallthrough;
case NL80211_IFTYPE_MESH_POINT:
/* no support for IGTK/BIGTK (yet?) */
return key_idx < 4;
case NL80211_IFTYPE_NAN_DATA:
/* these always need to support per-STA GTK */
return key_idx < 4;
case NL80211_IFTYPE_NAN:
/* no data */
if (key_idx < 4)
return false;
/* NAN reused this flag */
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION))
return key_idx <= 7;
return key_idx <= 5;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
/* see note about exception above */
if (mac_addr)
return false;
/* BIGTK support implies IGTK support */
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
return key_idx <= 7;
fallthrough;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
/* no RX with [B]IGTK */
if (mac_addr)
return false;
if (wiphy_ext_feature_isset(wdev->wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION))
return key_idx <= 7;
fallthrough;
case NL80211_IFTYPE_AP_VLAN:
/* no RX with GTK */
if (mac_addr)
return false;
if (cfg80211_igtk_cipher_supported(wdev->wiphy))
return key_idx <= 5;
return key_idx <= 3;
default:
return false;
}
}
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
@ -288,13 +345,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx,
bool pairwise, const u8 *mac_addr)
{
if (!cfg80211_valid_key_idx(rdev, key_idx, pairwise))
return -EINVAL;
if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
return -EINVAL;
if (pairwise && !mac_addr)
if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
return -EINVAL;
switch (params->cipher) {

View File

@ -454,8 +454,7 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
rejoin = true;
}
if (!pairwise && addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
if (!cfg80211_valid_key_idx(wdev, idx, pairwise, addr))
err = -ENOENT;
else
err = rdev_del_key(rdev, wdev, -1, idx, pairwise,