Currently, cfg80211 does not allow key installation, removal, or modification prior to association in non‑AP STA mode. However, Enhanced Privacy Protection Key Exchange (EPPKE) requires encryption keys to be managed before association. Introduce a new netlink attribute NL80211_ATTR_KEY_PREASSOC for NL80211_CMD_NEW_KEY, NL80211_CMD_DEL_KEY, and NL80211_CMD_SET_KEY to indicate that key installation, removal, or modification is permitted before association in non‑AP STA mode. Implement support in cfg80211 to manage keys before association when both the NL80211_ATTR_KEY_PREASSOC attribute and the NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION feature flag are set. Signed-off-by: Kavita Kavita --- include/uapi/linux/nl80211.h | 10 ++++++++++ net/wireless/nl80211.c | 30 ++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5c33486c6380..f78f6e7c4ab7 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2973,6 +2973,14 @@ enum nl80211_commands { * primary channel is 2 MHz wide, and the control channel designates * the 1 MHz primary subchannel within that 2 MHz primary. * + * @NL80211_ATTR_KEY_PREASSOC: flag attribute used with %NL80211_CMD_NEW_KEY, + * %NL80211_CMD_DEL_KEY and %NL80211_CMD_SET_KEY indicating that the key + * configuration commands should be allowed before the non-AP STA is + * associated. This is used in cases when the authentication method + * requires key installation/removal/modification before association + * (e.g., EPPKE). If set, transmit key data to the driver for installation/ + * removal/modification before the non-AP STA is associated. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3541,6 +3549,8 @@ enum nl80211_attrs { NL80211_ATTR_S1G_PRIMARY_2MHZ, + NL80211_ATTR_KEY_PREASSOC, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d7151fc5cf0e..f297639b916f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -932,6 +932,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_NESTED(nl80211_s1g_short_beacon), [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG }, + [NL80211_ATTR_KEY_PREASSOC] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -1659,7 +1660,8 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, return ERR_PTR(err); } -static int nl80211_key_allowed(struct wireless_dev *wdev) +static int nl80211_key_allowed(struct wireless_dev *wdev, + bool preassoc) { lockdep_assert_wiphy(wdev->wiphy); @@ -1675,7 +1677,10 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) return -ENOLINK; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - if (wdev->connected) + if (wdev->connected || + (preassoc && + wiphy_ext_feature_isset(wdev->wiphy, + NL80211_EXT_FEATURE_ASSOC_FRAME_ENCRYPTION))) return 0; return -ENOLINK; case NL80211_IFTYPE_NAN: @@ -4995,6 +5000,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; int link_id = nl80211_link_id_or_invalid(info->attrs); struct wireless_dev *wdev = dev->ieee80211_ptr; + bool preassoc; err = nl80211_parse_key(info, &key); if (err) @@ -5010,11 +5016,13 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) !(key.p.mode == NL80211_KEY_SET_TX)) return -EINVAL; + preassoc = nla_get_flag(info->attrs[NL80211_ATTR_KEY_PREASSOC]); + if (key.def) { if (!rdev->ops->set_default_key) return -EOPNOTSUPP; - err = nl80211_key_allowed(wdev); + err = nl80211_key_allowed(wdev, preassoc); if (err) return err; @@ -5039,7 +5047,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP; - err = nl80211_key_allowed(wdev); + err = nl80211_key_allowed(wdev, preassoc); if (err) return err; @@ -5062,7 +5070,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->set_default_beacon_key) return -EOPNOTSUPP; - err = nl80211_key_allowed(wdev); + err = nl80211_key_allowed(wdev, preassoc); if (err) return err; @@ -5103,6 +5111,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) const u8 *mac_addr = NULL; int link_id = nl80211_link_id_or_invalid(info->attrs); struct wireless_dev *wdev = dev->ieee80211_ptr; + bool preassoc; err = nl80211_parse_key(info, &key); if (err) @@ -5144,7 +5153,9 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - err = nl80211_key_allowed(wdev); + preassoc = nla_get_flag(info->attrs[NL80211_ATTR_KEY_PREASSOC]); + + err = nl80211_key_allowed(wdev, preassoc); if (err) GENL_SET_ERR_MSG(info, "key not allowed"); @@ -5172,6 +5183,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) struct key_parse key; int link_id = nl80211_link_id_or_invalid(info->attrs); struct wireless_dev *wdev = dev->ieee80211_ptr; + bool preassoc; err = nl80211_parse_key(info, &key); if (err) @@ -5199,7 +5211,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_key) return -EOPNOTSUPP; - err = nl80211_key_allowed(wdev); + preassoc = nla_get_flag(info->attrs[NL80211_ATTR_KEY_PREASSOC]); + + err = nl80211_key_allowed(wdev, preassoc); if (key.type == NL80211_KEYTYPE_GROUP && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) @@ -16781,7 +16795,7 @@ static int nl80211_set_qos_map(struct sk_buff *skb, memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); } - ret = nl80211_key_allowed(dev->ieee80211_ptr); + ret = nl80211_key_allowed(dev->ieee80211_ptr, false); if (!ret) ret = rdev_set_qos_map(rdev, dev, qos_map); -- 2.34.1