Commit ccb964b4 authored by Johannes Berg's avatar Johannes Berg

wifi: cfg80211: validate MLO connections better

When going into an MLO connection, validate that the link IDs
match what userspace indicated, and that the AP MLD addresses
and capabilities are all matching between the links.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Reviewed-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240102213313.ff83c034cb9a.I9962db0bfa8c73b37b8d5b59a3fad7f02f2129ae@changeid
[roll in extra fix from Miri to actually check the return value]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a8b65260
......@@ -4935,6 +4935,30 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
return sizeof(*mle) + common + mle->variable[0];
}
/**
* ieee80211_mle_get_link_id - returns the link ID
* @data: the basic multi link element
*
* The element is assumed to be of the correct type (BASIC) and big enough,
* this must be checked using ieee80211_mle_type_ok().
*
* If the BSS link ID can't be found, -1 will be returned
*/
static inline int ieee80211_mle_get_link_id(const u8 *data)
{
const struct ieee80211_multi_link_elem *mle = (const void *)data;
u16 control = le16_to_cpu(mle->control);
const u8 *common = mle->variable;
/* common points now at the beginning of ieee80211_mle_basic_common_info */
common += sizeof(struct ieee80211_mle_basic_common_info);
if (!(control & IEEE80211_MLC_BASIC_PRES_LINK_ID))
return -1;
return *common;
}
/**
* ieee80211_mle_get_bss_param_ch_cnt - returns the BSS parameter change count
* @mle: the basic multi link element
......
......@@ -362,7 +362,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
struct cfg80211_auth_request *req);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_assoc_request *req);
struct cfg80211_assoc_request *req,
struct netlink_ext_ack *extack);
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
......
......@@ -4,7 +4,7 @@
*
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015 Intel Deutschland GmbH
* Copyright (C) 2019-2020, 2022-2023 Intel Corporation
* Copyright (C) 2019-2020, 2022-2024 Intel Corporation
*/
#include <linux/kernel.h>
......@@ -325,28 +325,136 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
p1[i] &= p2[i];
}
/* Note: caller must cfg80211_put_bss() regardless of result */
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_assoc_request *req)
static int
cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a,
const struct ieee80211_multi_link_elem *mle_b,
struct netlink_ext_ack *extack)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err, i, j;
const struct ieee80211_mle_basic_common_info *common_a, *common_b;
lockdep_assert_wiphy(wdev->wiphy);
common_a = (const void *)mle_a->variable;
common_b = (const void *)mle_b->variable;
if (memcmp(common_a->mld_mac_addr, common_b->mld_mac_addr, ETH_ALEN)) {
NL_SET_ERR_MSG(extack, "AP MLD address mismatch");
return -EINVAL;
}
if (ieee80211_mle_get_eml_med_sync_delay((const u8 *)mle_a) !=
ieee80211_mle_get_eml_med_sync_delay((const u8 *)mle_b)) {
NL_SET_ERR_MSG(extack, "link EML medium sync delay mismatch");
return -EINVAL;
}
if (ieee80211_mle_get_eml_cap((const u8 *)mle_a) !=
ieee80211_mle_get_eml_cap((const u8 *)mle_b)) {
NL_SET_ERR_MSG(extack, "link EML capabilities mismatch");
return -EINVAL;
}
if (ieee80211_mle_get_mld_capa_op((const u8 *)mle_a) !=
ieee80211_mle_get_mld_capa_op((const u8 *)mle_b)) {
NL_SET_ERR_MSG(extack, "link MLD capabilities/ops mismatch");
return -EINVAL;
}
return 0;
}
static int cfg80211_mlme_check_mlo(struct net_device *dev,
struct cfg80211_assoc_request *req,
struct netlink_ext_ack *extack)
{
const struct ieee80211_multi_link_elem *mles[ARRAY_SIZE(req->links)] = {};
int i;
if (req->link_id < 0)
return 0;
if (!req->links[req->link_id].bss) {
NL_SET_ERR_MSG(extack, "no BSS for assoc link");
return -EINVAL;
}
rcu_read_lock();
for (i = 0; i < ARRAY_SIZE(req->links); i++) {
const struct cfg80211_bss_ies *ies;
const struct element *ml;
for (i = 1; i < ARRAY_SIZE(req->links); i++) {
if (!req->links[i].bss)
continue;
for (j = 0; j < i; j++) {
if (req->links[i].bss == req->links[j].bss)
return -EINVAL;
if (ether_addr_equal(req->links[i].bss->bssid, dev->dev_addr)) {
NL_SET_ERR_MSG(extack, "BSSID must not be our address");
req->links[i].error = -EINVAL;
goto error;
}
if (ether_addr_equal(req->links[i].bss->bssid, dev->dev_addr))
return -EINVAL;
ies = rcu_dereference(req->links[i].bss->ies);
ml = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK,
ies->data, ies->len);
if (!ml) {
NL_SET_ERR_MSG(extack, "MLO BSS w/o ML element");
req->links[i].error = -EINVAL;
goto error;
}
if (!ieee80211_mle_type_ok(ml->data + 1,
IEEE80211_ML_CONTROL_TYPE_BASIC,
ml->datalen - 1)) {
NL_SET_ERR_MSG(extack, "BSS with invalid ML element");
req->links[i].error = -EINVAL;
goto error;
}
mles[i] = (const void *)(ml->data + 1);
if (ieee80211_mle_get_link_id((const u8 *)mles[i]) != i) {
NL_SET_ERR_MSG(extack, "link ID mismatch");
req->links[i].error = -EINVAL;
goto error;
}
}
if (WARN_ON(!mles[req->link_id]))
goto error;
for (i = 0; i < ARRAY_SIZE(req->links); i++) {
if (i == req->link_id || !req->links[i].bss)
continue;
if (WARN_ON(!mles[i]))
goto error;
if (cfg80211_mlme_check_mlo_compat(mles[req->link_id], mles[i],
extack)) {
req->links[i].error = -EINVAL;
goto error;
}
}
rcu_read_unlock();
return 0;
error:
rcu_read_unlock();
return -EINVAL;
}
/* Note: caller must cfg80211_put_bss() regardless of result */
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_assoc_request *req,
struct netlink_ext_ack *extack)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
err = cfg80211_mlme_check_mlo(dev, req, extack);
if (err)
return err;
if (wdev->connected &&
(!req->prev_bssid ||
!ether_addr_equal(wdev->u.client.connected_addr, req->prev_bssid)))
......
......@@ -11245,7 +11245,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
struct nlattr *link;
int rem = 0;
err = cfg80211_mlme_assoc(rdev, dev, &req);
err = cfg80211_mlme_assoc(rdev, dev, &req,
info->extack);
if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
dev->ieee80211_ptr->conn_owner_nlportid =
......
......@@ -209,7 +209,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev,
if (!req.bss) {
err = -ENOENT;
} else {
err = cfg80211_mlme_assoc(rdev, wdev->netdev, &req);
err = cfg80211_mlme_assoc(rdev, wdev->netdev,
&req, NULL);
cfg80211_put_bss(&rdev->wiphy, req.bss);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment