Commit 77ee7c89 authored by Johannes Berg's avatar Johannes Berg

cfg80211: comprehensively check station changes

The station change API isn't being checked properly before
drivers are called, and as a result it is difficult to see
what should be allowed and what not.

In order to comprehensively check the API parameters parse
everything first, and then have the driver call a function
(cfg80211_check_station_change()) with the additionally
information about the kind of station that is being changed;
this allows the function to make better decisions than the
old code could.

While at it, also add a few checks, particularly in mesh
and clarify the TDLS station lifetime in documentation.

To be able to reduce a few checks, ignore any flag set bits
when the mask isn't set, they shouldn't be applied then.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent ff276691
...@@ -2990,13 +2990,15 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -2990,13 +2990,15 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
{ {
struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev);
int err;
if (vif->nw_type != AP_NETWORK) if (vif->nw_type != AP_NETWORK)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Use this only for authorizing/unauthorizing a station */ err = cfg80211_check_station_change(wiphy, params,
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) CFG80211_STA_AP_MLME_CLIENT);
return -EOPNOTSUPP; if (err)
return err;
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx,
......
...@@ -677,6 +677,49 @@ struct station_parameters { ...@@ -677,6 +677,49 @@ struct station_parameters {
u8 ext_capab_len; u8 ext_capab_len;
}; };
/**
* enum cfg80211_station_type - the type of station being modified
* @CFG80211_STA_AP_CLIENT: client of an AP interface
* @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
* the AP MLME in the device
* @CFG80211_STA_AP_STA: AP station on managed interface
* @CFG80211_STA_IBSS: IBSS station
* @CFG80211_STA_TDLS_PEER_SETUP: TDLS peer on managed interface (dummy entry
* while TDLS setup is in progress, it moves out of this state when
* being marked authorized; use this only if TDLS with external setup is
* supported/used)
* @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active
* entry that is operating, has been marked authorized by userspace)
* @CFG80211_STA_MESH_PEER_NONSEC: peer on mesh interface (non-secured)
* @CFG80211_STA_MESH_PEER_SECURE: peer on mesh interface (secured)
*/
enum cfg80211_station_type {
CFG80211_STA_AP_CLIENT,
CFG80211_STA_AP_MLME_CLIENT,
CFG80211_STA_AP_STA,
CFG80211_STA_IBSS,
CFG80211_STA_TDLS_PEER_SETUP,
CFG80211_STA_TDLS_PEER_ACTIVE,
CFG80211_STA_MESH_PEER_NONSEC,
CFG80211_STA_MESH_PEER_SECURE,
};
/**
* cfg80211_check_station_change - validate parameter changes
* @wiphy: the wiphy this operates on
* @params: the new parameters for a station
* @statype: the type of station being modified
*
* Utility function for the @change_station driver method. Call this function
* with the appropriate station type looking up the station (and checking that
* it exists). It will verify whether the station change is acceptable, and if
* not will return an error code. Note that it may modify the parameters for
* backward compatibility reasons, so don't use them before calling this.
*/
int cfg80211_check_station_change(struct wiphy *wiphy,
struct station_parameters *params,
enum cfg80211_station_type statype);
/** /**
* enum station_info_flags - station information flags * enum station_info_flags - station information flags
* *
...@@ -1770,9 +1813,8 @@ struct cfg80211_gtk_rekey_data { ...@@ -1770,9 +1813,8 @@ struct cfg80211_gtk_rekey_data {
* @change_station: Modify a given station. Note that flags changes are not much * @change_station: Modify a given station. Note that flags changes are not much
* validated in cfg80211, in particular the auth/assoc/authorized flags * validated in cfg80211, in particular the auth/assoc/authorized flags
* might come to the driver in invalid combinations -- make sure to check * might come to the driver in invalid combinations -- make sure to check
* them, also against the existing state! Also, supported_rates changes are * them, also against the existing state! Drivers must call
* not checked in station mode -- drivers need to reject (or ignore) them * cfg80211_check_station_change() to validate the information.
* for anything but TDLS peers.
* @get_station: get station information for the station identified by @mac * @get_station: get station information for the station identified by @mac
* @dump_station: dump station callback -- resume dump at index @idx * @dump_station: dump station callback -- resume dump at index @idx
* *
......
...@@ -36,7 +36,21 @@ ...@@ -36,7 +36,21 @@
* The station is still assumed to belong to the AP interface it was added * The station is still assumed to belong to the AP interface it was added
* to. * to.
* *
* TODO: need more info? * Station handling varies per interface type and depending on the driver's
* capabilities.
*
* For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
* and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
* - a setup station entry is added, not yet authorized, without any rate
* or capability information, this just exists to avoid race conditions
* - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
* to add rate and capability information to the station and at the same
* time mark it authorized.
* - %NL80211_TDLS_ENABLE_LINK is then used
* - after this, the only valid operation is to remove it by tearing down
* the TDLS link (%NL80211_TDLS_DISABLE_LINK)
*
* TODO: need more info for other interface types
*/ */
/** /**
......
...@@ -1177,6 +1177,18 @@ static int sta_apply_parameters(struct ieee80211_local *local, ...@@ -1177,6 +1177,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
set |= BIT(NL80211_STA_FLAG_ASSOCIATED); set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
/*
* TDLS -- everything follows authorized, but
* only becoming authorized is possible, not
* going back
*/
if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED);
mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED);
}
} }
ret = sta_apply_auth_flags(local, sta, mask, set); ret = sta_apply_auth_flags(local, sta, mask, set);
...@@ -1261,9 +1273,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, ...@@ -1261,9 +1273,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
u32 changed = 0; u32 changed = 0;
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED &&
(params->sta_modify_mask & if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
STATION_PARAM_APPLY_PLINK_STATE)) {
switch (params->plink_state) { switch (params->plink_state) {
case NL80211_PLINK_ESTAB: case NL80211_PLINK_ESTAB:
if (sta->plink_state != NL80211_PLINK_ESTAB) if (sta->plink_state != NL80211_PLINK_ESTAB)
...@@ -1294,21 +1305,18 @@ static int sta_apply_parameters(struct ieee80211_local *local, ...@@ -1294,21 +1305,18 @@ static int sta_apply_parameters(struct ieee80211_local *local,
/* nothing */ /* nothing */
break; break;
} }
} else if (params->sta_modify_mask & }
STATION_PARAM_APPLY_PLINK_STATE) {
return -EINVAL; switch (params->plink_action) {
} else { case NL80211_PLINK_ACTION_NO_ACTION:
switch (params->plink_action) { /* nothing */
case NL80211_PLINK_ACTION_NO_ACTION: break;
/* nothing */ case NL80211_PLINK_ACTION_OPEN:
break; changed |= mesh_plink_open(sta);
case NL80211_PLINK_ACTION_OPEN: break;
changed |= mesh_plink_open(sta); case NL80211_PLINK_ACTION_BLOCK:
break; changed |= mesh_plink_block(sta);
case NL80211_PLINK_ACTION_BLOCK: break;
changed |= mesh_plink_block(sta);
break;
}
} }
if (params->local_pm) if (params->local_pm)
...@@ -1354,8 +1362,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -1354,8 +1362,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
* defaults -- if userspace wants something else we'll * defaults -- if userspace wants something else we'll
* change it accordingly in sta_apply_parameters() * change it accordingly in sta_apply_parameters()
*/ */
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
}
err = sta_apply_parameters(local, sta, params); err = sta_apply_parameters(local, sta, params);
if (err) { if (err) {
...@@ -1364,8 +1374,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -1364,8 +1374,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
} }
/* /*
* for TDLS, rate control should be initialized only when supported * for TDLS, rate control should be initialized only when
* rates are known. * rates are known and station is marked authorized
*/ */
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
rate_control_rate_init(sta); rate_control_rate_init(sta);
...@@ -1402,50 +1412,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, ...@@ -1402,50 +1412,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
} }
static int ieee80211_change_station(struct wiphy *wiphy, static int ieee80211_change_station(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev, u8 *mac,
u8 *mac,
struct station_parameters *params) struct station_parameters *params)
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_local *local = wiphy_priv(wiphy);
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_sub_if_data *vlansdata; struct ieee80211_sub_if_data *vlansdata;
enum cfg80211_station_type statype;
int err; int err;
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, mac); sta = sta_info_get_bss(sdata, mac);
if (!sta) { if (!sta) {
mutex_unlock(&local->sta_mtx); err = -ENOENT;
return -ENOENT; goto out_err;
} }
/* in station mode, some updates are only valid with TDLS */ switch (sdata->vif.type) {
if (sdata->vif.type == NL80211_IFTYPE_STATION && case NL80211_IFTYPE_MESH_POINT:
(params->supported_rates || params->ht_capa || params->vht_capa || if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
params->sta_modify_mask || statype = CFG80211_STA_MESH_PEER_SECURE;
(params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) && else
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { statype = CFG80211_STA_MESH_PEER_NONSEC;
mutex_unlock(&local->sta_mtx); break;
return -EINVAL; case NL80211_IFTYPE_ADHOC:
statype = CFG80211_STA_IBSS;
break;
case NL80211_IFTYPE_STATION:
if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
statype = CFG80211_STA_AP_STA;
break;
}
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
statype = CFG80211_STA_TDLS_PEER_ACTIVE;
else
statype = CFG80211_STA_TDLS_PEER_SETUP;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
statype = CFG80211_STA_AP_CLIENT;
break;
default:
err = -EOPNOTSUPP;
goto out_err;
} }
err = cfg80211_check_station_change(wiphy, params, statype);
if (err)
goto out_err;
if (params->vlan && params->vlan != sta->sdata->dev) { if (params->vlan && params->vlan != sta->sdata->dev) {
bool prev_4addr = false; bool prev_4addr = false;
bool new_4addr = false; bool new_4addr = false;
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
vlansdata->vif.type != NL80211_IFTYPE_AP) {
mutex_unlock(&local->sta_mtx);
return -EINVAL;
}
if (params->vlan->ieee80211_ptr->use_4addr) { if (params->vlan->ieee80211_ptr->use_4addr) {
if (vlansdata->u.vlan.sta) { if (vlansdata->u.vlan.sta) {
mutex_unlock(&local->sta_mtx); err = -EBUSY;
return -EBUSY; goto out_err;
} }
rcu_assign_pointer(vlansdata->u.vlan.sta, sta); rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
...@@ -1472,12 +1499,12 @@ static int ieee80211_change_station(struct wiphy *wiphy, ...@@ -1472,12 +1499,12 @@ static int ieee80211_change_station(struct wiphy *wiphy,
} }
err = sta_apply_parameters(local, sta, params); err = sta_apply_parameters(local, sta, params);
if (err) { if (err)
mutex_unlock(&local->sta_mtx); goto out_err;
return err;
}
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) /* When peer becomes authorized, init rate control as well */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
test_sta_flag(sta, WLAN_STA_AUTHORIZED))
rate_control_rate_init(sta); rate_control_rate_init(sta);
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
...@@ -1487,7 +1514,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, ...@@ -1487,7 +1514,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
ieee80211_recalc_ps(local, -1); ieee80211_recalc_ps(local, -1);
ieee80211_recalc_ps_vif(sdata); ieee80211_recalc_ps_vif(sdata);
} }
return 0; return 0;
out_err:
mutex_unlock(&local->sta_mtx);
return err;
} }
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
......
...@@ -2967,6 +2967,7 @@ static int parse_station_flags(struct genl_info *info, ...@@ -2967,6 +2967,7 @@ static int parse_station_flags(struct genl_info *info,
sta_flags = nla_data(nla); sta_flags = nla_data(nla);
params->sta_flags_mask = sta_flags->mask; params->sta_flags_mask = sta_flags->mask;
params->sta_flags_set = sta_flags->set; params->sta_flags_set = sta_flags->set;
params->sta_flags_set &= params->sta_flags_mask;
if ((params->sta_flags_mask | if ((params->sta_flags_mask |
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
return -EINVAL; return -EINVAL;
...@@ -3320,6 +3321,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3320,6 +3321,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
return genlmsg_reply(msg, info); return genlmsg_reply(msg, info);
} }
int cfg80211_check_station_change(struct wiphy *wiphy,
struct station_parameters *params,
enum cfg80211_station_type statype)
{
if (params->listen_interval != -1)
return -EINVAL;
if (params->aid)
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
switch (statype) {
case CFG80211_STA_MESH_PEER_NONSEC:
case CFG80211_STA_MESH_PEER_SECURE:
/*
* No ignoring the TDLS flag here -- the userspace mesh
* code doesn't have the bug of including TDLS in the
* mask everywhere.
*/
if (params->sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHORIZED)))
return -EINVAL;
break;
case CFG80211_STA_TDLS_PEER_SETUP:
case CFG80211_STA_TDLS_PEER_ACTIVE:
if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
return -EINVAL;
/* ignore since it can't change */
params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
break;
default:
/* disallow mesh-specific things */
if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
return -EINVAL;
if (params->local_pm)
return -EINVAL;
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
return -EINVAL;
}
if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
/* TDLS can't be set, ... */
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
return -EINVAL;
/*
* ... but don't bother the driver with it. This works around
* a hostapd/wpa_supplicant issue -- it always includes the
* TLDS_PEER flag in the mask even for AP mode.
*/
params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
}
if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
/* reject other things that can't change */
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
return -EINVAL;
if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
return -EINVAL;
if (params->supported_rates)
return -EINVAL;
if (params->ext_capab || params->ht_capa || params->vht_capa)
return -EINVAL;
}
if (statype != CFG80211_STA_AP_CLIENT) {
if (params->vlan)
return -EINVAL;
}
switch (statype) {
case CFG80211_STA_AP_MLME_CLIENT:
/* Use this only for authorizing/unauthorizing a station */
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
return -EOPNOTSUPP;
break;
case CFG80211_STA_AP_CLIENT:
/* accept only the listed bits */
if (params->sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP)))
return -EINVAL;
/* but authenticated/associated only if driver handles it */
if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
params->sta_flags_mask &
(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED)))
return -EINVAL;
break;
case CFG80211_STA_IBSS:
case CFG80211_STA_AP_STA:
/* reject any changes other than AUTHORIZED */
if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL;
break;
case CFG80211_STA_TDLS_PEER_SETUP:
/* reject any changes other than AUTHORIZED or WME */
if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
BIT(NL80211_STA_FLAG_WME)))
return -EINVAL;
/* force (at least) rates when authorizing */
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
!params->supported_rates)
return -EINVAL;
break;
case CFG80211_STA_TDLS_PEER_ACTIVE:
/* reject any changes */
return -EINVAL;
case CFG80211_STA_MESH_PEER_NONSEC:
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
return -EINVAL;
break;
case CFG80211_STA_MESH_PEER_SECURE:
if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
return -EINVAL;
break;
}
return 0;
}
EXPORT_SYMBOL(cfg80211_check_station_change);
/* /*
* Get vlan interface making sure it is running and on the right wiphy. * Get vlan interface making sure it is running and on the right wiphy.
*/ */
...@@ -3342,6 +3473,13 @@ static struct net_device *get_vlan(struct genl_info *info, ...@@ -3342,6 +3473,13 @@ static struct net_device *get_vlan(struct genl_info *info,
goto error; goto error;
} }
if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
ret = -EINVAL;
goto error;
}
if (!netif_running(v)) { if (!netif_running(v)) {
ret = -ENETDOWN; ret = -ENETDOWN;
goto error; goto error;
...@@ -3410,15 +3548,18 @@ static int nl80211_set_station_tdls(struct genl_info *info, ...@@ -3410,15 +3548,18 @@ static int nl80211_set_station_tdls(struct genl_info *info,
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
int err;
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct station_parameters params; struct station_parameters params;
u8 *mac_addr = NULL; u8 *mac_addr;
int err;
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
params.listen_interval = -1; params.listen_interval = -1;
if (!rdev->ops->change_station)
return -EOPNOTSUPP;
if (info->attrs[NL80211_ATTR_STA_AID]) if (info->attrs[NL80211_ATTR_STA_AID])
return -EINVAL; return -EINVAL;
...@@ -3450,9 +3591,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3450,9 +3591,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
return -EINVAL; return -EINVAL;
if (!rdev->ops->change_station)
return -EOPNOTSUPP;
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params)) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
return -EINVAL; return -EINVAL;
...@@ -3482,133 +3620,33 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3482,133 +3620,33 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.local_pm = pm; params.local_pm = pm;
} }
/* Include parameters for TDLS peer (will check later) */
err = nl80211_set_station_tdls(info, &params);
if (err)
return err;
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
switch (dev->ieee80211_ptr->iftype) { switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_GO:
/* disallow mesh-specific things */
if (params.plink_action)
return -EINVAL;
if (params.local_pm)
return -EINVAL;
if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
return -EINVAL;
/* TDLS can't be set, ... */
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
return -EINVAL;
/*
* ... but don't bother the driver with it. This works around
* a hostapd/wpa_supplicant issue -- it always includes the
* TLDS_PEER flag in the mask even for AP mode.
*/
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
/* accept only the listed bits */
if (params.sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP)))
return -EINVAL;
/* but authenticated/associated only if driver handles it */
if (!(rdev->wiphy.features &
NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
params.sta_flags_mask &
(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED)))
return -EINVAL;
/* reject other things that can't change */
if (params.supported_rates)
return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
return -EINVAL;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
return -EINVAL;
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
break;
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
/*
* Don't allow userspace to change the TDLS_PEER flag,
* but silently ignore attempts to change it since we
* don't have state here to verify that it doesn't try
* to change the flag.
*/
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
/* Include parameters for TDLS peer (driver will check) */
err = nl80211_set_station_tdls(info, &params);
if (err)
return err;
/* disallow things sta doesn't support */
if (params.plink_action)
return -EINVAL;
if (params.local_pm)
return -EINVAL;
if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
return -EINVAL;
/* reject any changes other than AUTHORIZED or WME (for TDLS) */
if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
BIT(NL80211_STA_FLAG_WME)))
return -EINVAL;
break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
/* disallow things sta doesn't support */
if (params.plink_action)
return -EINVAL;
if (params.local_pm)
return -EINVAL;
if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
return -EINVAL;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
return -EINVAL;
/* reject any changes other than AUTHORIZED */
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL;
break;
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
/* disallow things mesh doesn't support */
if (params.vlan)
return -EINVAL;
if (params.supported_rates)
return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
return -EINVAL;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
return -EINVAL;
/*
* No special handling for TDLS here -- the userspace
* mesh code doesn't have this bug.
*/
if (params.sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHORIZED)))
return -EINVAL;
break; break;
default: default:
return -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out_put_vlan;
} }
/* be aware of params.vlan when changing code here */ /* driver will call cfg80211_check_station_change() */
err = rdev_change_station(rdev, dev, mac_addr, &params); err = rdev_change_station(rdev, dev, mac_addr, &params);
out_put_vlan:
if (params.vlan) if (params.vlan)
dev_put(params.vlan); dev_put(params.vlan);
...@@ -3687,6 +3725,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3687,6 +3725,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params)) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
return -EINVAL; return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
switch (dev->ieee80211_ptr->iftype) { switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
...@@ -3730,8 +3771,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3730,8 +3771,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* ignore uAPSD data */ /* ignore uAPSD data */
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
/* associated is disallowed */ /* these are disallowed */
if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) if (params.sta_flags_mask &
(BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_AUTHENTICATED)))
return -EINVAL; return -EINVAL;
/* Only TDLS peers can be added */ /* Only TDLS peers can be added */
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
...@@ -3742,6 +3785,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3742,6 +3785,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* ... with external setup is supported */ /* ... with external setup is supported */
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/*
* Older wpa_supplicant versions always mark the TDLS peer
* as authorized, but it shouldn't yet be.
*/
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
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