summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2026-07-15 21:24:40 +0300
committerJohannes Berg <johannes.berg@intel.com>2026-07-21 19:18:53 +0200
commit8ff047b9c7b2900ec6e49361f81d74ac61563cdc (patch)
tree9ab05e24787316a71e580a3ea439901380037e1f
parent3002812cbedb09a359e0865670d7ae6267b1dd6f (diff)
downloadlinux-8ff047b9c7b2900ec6e49361f81d74ac61563cdc.tar.gz
linux-8ff047b9c7b2900ec6e49361f81d74ac61563cdc.zip
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>
-rw-r--r--net/wireless/core.h5
-rw-r--r--net/wireless/nl80211.c11
-rw-r--r--net/wireless/util.c105
-rw-r--r--net/wireless/wext-compat.c3
4 files changed, 86 insertions, 38 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h
index df47ed6208a5..399037514943 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -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,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5fa70974d57c..4044b5505104 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -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);
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 24527bf321b2..3e584d0ca3e2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -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 (WARN_ON(!wdev))
+ return false;
- if (key_idx < 0 || key_idx > max_key_idx)
+ if (key_idx < 0)
return false;
- return true;
+ /*
+ * 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) {
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 5dbf3ef4b257..d45bc08c0de4 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -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,