Commit bdd90d5e authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

cfg80211: validate nl80211 station handling better

The nl80211 station handling code is a bit messy
and doesn't do a lot of validation. It seems like
this could be an issue for drivers that don't use
mac80211 to validate everything.

As cfg80211 doesn't keep station state, move the
validation of allowing supported_rates to change
for TDLS only in station mode to mac80211.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d83023da
...@@ -1536,7 +1536,11 @@ enum nl80211_iftype { ...@@ -1536,7 +1536,11 @@ enum nl80211_iftype {
* @NL80211_STA_FLAG_WME: station is WME/QoS capable * @NL80211_STA_FLAG_WME: station is WME/QoS capable
* @NL80211_STA_FLAG_MFP: station uses management frame protection * @NL80211_STA_FLAG_MFP: station uses management frame protection
* @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
* @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should
* only be used in managed mode (even in the flags mask). Note that the
* flag can't be changed, it is only valid while adding a station, and
* attempts to change it will silently be ignored (rather than rejected
* as errors.)
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use * @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/ */
......
...@@ -1346,7 +1346,12 @@ struct cfg80211_gtk_rekey_data { ...@@ -1346,7 +1346,12 @@ struct cfg80211_gtk_rekey_data {
* *
* @add_station: Add a new station. * @add_station: Add a new station.
* @del_station: Remove a station; @mac may be NULL to remove all stations. * @del_station: Remove a station; @mac may be NULL to remove all stations.
* @change_station: Modify a given station. * @change_station: Modify a given station. Note that flags changes are not much
* validated in cfg80211, in particular the auth/assoc/authorized flags
* might come to the driver in invalid combinations -- make sure to check
* them, also against the existing state! Also, supported_rates changes are
* not checked in station mode -- drivers need to reject (or ignore) them
* 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
* *
......
...@@ -976,6 +976,14 @@ static int ieee80211_change_station(struct wiphy *wiphy, ...@@ -976,6 +976,14 @@ static int ieee80211_change_station(struct wiphy *wiphy,
return -EINVAL; return -EINVAL;
} }
/* in station mode, supported rates are only valid with TDLS */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
params->supported_rates &&
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
rcu_read_unlock();
return -EINVAL;
}
if (params->vlan && params->vlan != sta->sdata->dev) { if (params->vlan && params->vlan != sta->sdata->dev) {
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
......
...@@ -2579,6 +2579,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -2579,6 +2579,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.ht_capa = params.ht_capa =
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
if (!rdev->ops->change_station)
return -EOPNOTSUPP;
if (parse_station_flags(info, &params)) if (parse_station_flags(info, &params))
return -EINVAL; return -EINVAL;
...@@ -2590,73 +2593,84 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -2590,73 +2593,84 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_state = params.plink_state =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan);
/* validate settings */
err = 0;
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 */ /* disallow mesh-specific things */
if (params.plink_action) if (params.plink_action)
err = -EINVAL; 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_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP)))
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; break;
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
/* disallow things sta doesn't support */ /* disallow things sta doesn't support */
if (params.plink_action) if (params.plink_action)
err = -EINVAL; return -EINVAL;
if (params.vlan)
err = -EINVAL;
if (params.supported_rates &&
!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
err = -EINVAL;
if (params.ht_capa) if (params.ht_capa)
err = -EINVAL; return -EINVAL;
if (params.listen_interval >= 0) if (params.listen_interval >= 0)
err = -EINVAL; return -EINVAL;
if (params.sta_flags_mask & /*
~(BIT(NL80211_STA_FLAG_AUTHORIZED) | * Don't allow userspace to change the TDLS_PEER flag,
BIT(NL80211_STA_FLAG_TDLS_PEER))) * but silently ignore attempts to change it since we
err = -EINVAL; * don't have state here to verify that it doesn't try
/* can't change the TDLS bit */ * to change the flag.
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && */
(params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
err = -EINVAL;
/* reject any changes other than AUTHORIZED */
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL;
break; break;
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
/* disallow things mesh doesn't support */ /* disallow things mesh doesn't support */
if (params.vlan) if (params.vlan)
err = -EINVAL; return -EINVAL;
if (params.ht_capa) if (params.ht_capa)
err = -EINVAL; return -EINVAL;
if (params.listen_interval >= 0) if (params.listen_interval >= 0)
err = -EINVAL; return -EINVAL;
/*
* No special handling for TDLS here -- the userspace
* mesh code doesn't have this bug.
*/
if (params.sta_flags_mask & if (params.sta_flags_mask &
~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_MFP) | BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHORIZED))) BIT(NL80211_STA_FLAG_AUTHORIZED)))
err = -EINVAL; return -EINVAL;
break; break;
default: default:
err = -EINVAL; return -EOPNOTSUPP;
} }
if (err) /* be aware of params.vlan when changing code here */
goto out;
if (!rdev->ops->change_station) {
err = -EOPNOTSUPP;
goto out;
}
err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params); err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
out:
if (params.vlan) if (params.vlan)
dev_put(params.vlan); dev_put(params.vlan);
...@@ -2711,9 +2725,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ...@@ -2711,9 +2725,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.plink_action = params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
if (!rdev->ops->add_station)
return -EOPNOTSUPP;
if (parse_station_flags(info, &params)) if (parse_station_flags(info, &params))
return -EINVAL; return -EINVAL;
switch (dev->ieee80211_ptr->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
/* parse WME attributes if sta is WME capable */ /* parse WME attributes if sta is WME capable */
if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
...@@ -2730,51 +2751,55 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) ...@@ -2730,51 +2751,55 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (tb[NL80211_STA_WME_UAPSD_QUEUES]) if (tb[NL80211_STA_WME_UAPSD_QUEUES])
params.uapsd_queues = params.uapsd_queues =
nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]); nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) if (params.uapsd_queues &
~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
return -EINVAL; return -EINVAL;
if (tb[NL80211_STA_WME_MAX_SP]) if (tb[NL80211_STA_WME_MAX_SP])
params.max_sp = params.max_sp =
nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) if (params.max_sp &
~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
return -EINVAL; return -EINVAL;
params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
} }
/* TDLS peers cannot be added */
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
return -EINVAL;
/*
* Only managed stations can add TDLS peers, and only when the
* wiphy supports external TDLS setup.
*/
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
!((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
return -EINVAL; return -EINVAL;
/* but don't bother the driver with it */
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev); params.vlan = get_vlan(info, rdev);
if (IS_ERR(params.vlan)) if (IS_ERR(params.vlan))
return PTR_ERR(params.vlan); return PTR_ERR(params.vlan);
break;
/* validate settings */ case NL80211_IFTYPE_MESH_POINT:
err = 0; /* TDLS peers cannot be added */
if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
if (!rdev->ops->add_station) { return -EINVAL;
err = -EOPNOTSUPP; break;
goto out; case NL80211_IFTYPE_STATION:
/* Only TDLS peers can be added */
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
return -EINVAL;
/* Can only add if TDLS ... */
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
return -EOPNOTSUPP;
/* ... with external setup is supported */
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
return -EOPNOTSUPP;
break;
default:
return -EOPNOTSUPP;
} }
/* be aware of params.vlan when changing code here */
err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params); err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
out:
if (params.vlan) if (params.vlan)
dev_put(params.vlan); dev_put(params.vlan);
return err; return err;
......
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