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

cfg80211/mac80211: allow per-station GTKs

This adds API to allow adding per-station GTKs,
updates mac80211 to support it, and also allows
drivers to remove a key from hwaccel again when
this may be necessary due to multiple GTKs.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 53f73c09
...@@ -161,7 +161,7 @@ static int iwm_key_init(struct iwm_key *key, u8 key_index, ...@@ -161,7 +161,7 @@ static int iwm_key_init(struct iwm_key *key, u8 key_index,
} }
static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, const u8 *mac_addr, u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params) struct key_params *params)
{ {
struct iwm_priv *iwm = ndev_to_iwm(ndev); struct iwm_priv *iwm = ndev_to_iwm(ndev);
...@@ -181,7 +181,8 @@ static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, ...@@ -181,7 +181,8 @@ static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
} }
static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, const u8 *mac_addr, void *cookie, u8 key_index, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie, void (*callback)(void *cookie,
struct key_params*)) struct key_params*))
{ {
...@@ -206,7 +207,7 @@ static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, ...@@ -206,7 +207,7 @@ static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
u8 key_index, const u8 *mac_addr) u8 key_index, bool pairwise, const u8 *mac_addr)
{ {
struct iwm_priv *iwm = ndev_to_iwm(ndev); struct iwm_priv *iwm = ndev_to_iwm(ndev);
struct iwm_key *key = &iwm->keys[key_index]; struct iwm_key *key = &iwm->keys[key_index];
......
...@@ -1438,7 +1438,7 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy, ...@@ -1438,7 +1438,7 @@ static int lbs_cfg_set_default_key(struct wiphy *wiphy,
static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
u8 idx, const u8 *mac_addr, u8 idx, bool pairwise, const u8 *mac_addr,
struct key_params *params) struct key_params *params)
{ {
struct lbs_private *priv = wiphy_priv(wiphy); struct lbs_private *priv = wiphy_priv(wiphy);
...@@ -1498,7 +1498,7 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, ...@@ -1498,7 +1498,7 @@ static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr) u8 key_index, bool pairwise, const u8 *mac_addr)
{ {
lbs_deb_enter(LBS_DEB_CFG80211); lbs_deb_enter(LBS_DEB_CFG80211);
......
...@@ -540,11 +540,11 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev, ...@@ -540,11 +540,11 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); struct ieee80211_channel *chan, enum nl80211_channel_type channel_type);
static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr, u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params); struct key_params *params);
static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr); u8 key_index, bool pairwise, const u8 *mac_addr);
static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index); u8 key_index);
...@@ -2308,7 +2308,7 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev, ...@@ -2308,7 +2308,7 @@ static int rndis_set_channel(struct wiphy *wiphy, struct net_device *netdev,
} }
static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr, u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params) struct key_params *params)
{ {
struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct rndis_wlan_private *priv = wiphy_priv(wiphy);
...@@ -2344,7 +2344,7 @@ static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, ...@@ -2344,7 +2344,7 @@ static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev,
} }
static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr) u8 key_index, bool pairwise, const u8 *mac_addr)
{ {
struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct rndis_wlan_private *priv = wiphy_priv(wiphy);
struct usbnet *usbdev = priv->usbdev; struct usbnet *usbdev = priv->usbdev;
......
...@@ -801,6 +801,9 @@ enum nl80211_commands { ...@@ -801,6 +801,9 @@ enum nl80211_commands {
* This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
* for non-automatic settings. * for non-automatic settings.
* *
* @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
* means support for per-station GTKs.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -968,6 +971,8 @@ enum nl80211_attrs { ...@@ -968,6 +971,8 @@ enum nl80211_attrs {
NL80211_ATTR_CONTROL_PORT_ETHERTYPE, NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
NL80211_ATTR_SUPPORT_IBSS_RSN,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
...@@ -1659,11 +1664,14 @@ enum nl80211_auth_type { ...@@ -1659,11 +1664,14 @@ enum nl80211_auth_type {
* @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
* @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
* @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
* @NUM_NL80211_KEYTYPES: number of defined key types
*/ */
enum nl80211_key_type { enum nl80211_key_type {
NL80211_KEYTYPE_GROUP, NL80211_KEYTYPE_GROUP,
NL80211_KEYTYPE_PAIRWISE, NL80211_KEYTYPE_PAIRWISE,
NL80211_KEYTYPE_PEERKEY, NL80211_KEYTYPE_PEERKEY,
NUM_NL80211_KEYTYPES
}; };
/** /**
...@@ -1694,6 +1702,9 @@ enum nl80211_wpa_versions { ...@@ -1694,6 +1702,9 @@ enum nl80211_wpa_versions {
* CCMP keys, each six bytes in little endian * CCMP keys, each six bytes in little endian
* @NL80211_KEY_DEFAULT: flag indicating default key * @NL80211_KEY_DEFAULT: flag indicating default key
* @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
* @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
* specified the default depends on whether a MAC address was
* given with the command using the key or not (u32)
* @__NL80211_KEY_AFTER_LAST: internal * @__NL80211_KEY_AFTER_LAST: internal
* @NL80211_KEY_MAX: highest key attribute * @NL80211_KEY_MAX: highest key attribute
*/ */
...@@ -1705,6 +1716,7 @@ enum nl80211_key_attributes { ...@@ -1705,6 +1716,7 @@ enum nl80211_key_attributes {
NL80211_KEY_SEQ, NL80211_KEY_SEQ,
NL80211_KEY_DEFAULT, NL80211_KEY_DEFAULT,
NL80211_KEY_DEFAULT_MGMT, NL80211_KEY_DEFAULT_MGMT,
NL80211_KEY_TYPE,
/* keep last */ /* keep last */
__NL80211_KEY_AFTER_LAST, __NL80211_KEY_AFTER_LAST,
......
...@@ -1130,13 +1130,14 @@ struct cfg80211_ops { ...@@ -1130,13 +1130,14 @@ struct cfg80211_ops {
struct vif_params *params); struct vif_params *params);
int (*add_key)(struct wiphy *wiphy, struct net_device *netdev, int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr, u8 key_index, bool pairwise, const u8 *mac_addr,
struct key_params *params); struct key_params *params);
int (*get_key)(struct wiphy *wiphy, struct net_device *netdev, int (*get_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr, void *cookie, u8 key_index, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie, struct key_params*)); void (*callback)(void *cookie, struct key_params*));
int (*del_key)(struct wiphy *wiphy, struct net_device *netdev, int (*del_key)(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index, const u8 *mac_addr); u8 key_index, bool pairwise, const u8 *mac_addr);
int (*set_default_key)(struct wiphy *wiphy, int (*set_default_key)(struct wiphy *wiphy,
struct net_device *netdev, struct net_device *netdev,
u8 key_index); u8 key_index);
...@@ -1304,6 +1305,7 @@ struct cfg80211_ops { ...@@ -1304,6 +1305,7 @@ struct cfg80211_ops {
* @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the * @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the
* control port protocol ethertype. The device also honours the * control port protocol ethertype. The device also honours the
* control_port_no_encrypt flag. * control_port_no_encrypt flag.
* @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN.
*/ */
enum wiphy_flags { enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
...@@ -1314,6 +1316,7 @@ enum wiphy_flags { ...@@ -1314,6 +1316,7 @@ enum wiphy_flags {
WIPHY_FLAG_4ADDR_AP = BIT(5), WIPHY_FLAG_4ADDR_AP = BIT(5),
WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_4ADDR_STATION = BIT(6),
WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7),
WIPHY_FLAG_IBSS_RSN = BIT(7),
}; };
struct mac_address { struct mac_address {
......
...@@ -1041,6 +1041,13 @@ enum ieee80211_tkip_key_type { ...@@ -1041,6 +1041,13 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_NEED_DTIM_PERIOD: * @IEEE80211_HW_NEED_DTIM_PERIOD:
* This device needs to know the DTIM period for the BSS before * This device needs to know the DTIM period for the BSS before
* associating. * associating.
*
* @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports
* per-station GTKs as used by IBSS RSN or during fast transition. If
* the device doesn't support per-station GTKs, but can be asked not
* to decrypt group addressed frames, then IBSS RSN support is still
* possible but software crypto will be used. Advertise the wiphy flag
* only in that case.
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
...@@ -1064,6 +1071,7 @@ enum ieee80211_hw_flags { ...@@ -1064,6 +1071,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19, IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20,
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
}; };
/** /**
...@@ -2582,6 +2590,22 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success); ...@@ -2582,6 +2590,22 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success);
void ieee80211_request_smps(struct ieee80211_vif *vif, void ieee80211_request_smps(struct ieee80211_vif *vif,
enum ieee80211_smps_mode smps_mode); enum ieee80211_smps_mode smps_mode);
/**
* ieee80211_key_removed - disable hw acceleration for key
* @key_conf: The key hw acceleration should be disabled for
*
* This allows drivers to indicate that the given key has been
* removed from hardware acceleration, due to a new key that
* was added. Don't use this if the key can continue to be used
* for TX, if the key restriction is on RX only it is permitted
* to keep the key for TX only and not call this function.
*
* Due to locking constraints, it may only be called during
* @set_key. This function must be allowed to sleep, and the
* key it tries to disable may still be used until it returns.
*/
void ieee80211_key_removed(struct ieee80211_key_conf *key_conf);
/* Rate control API */ /* Rate control API */
/** /**
......
...@@ -103,7 +103,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, ...@@ -103,7 +103,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
} }
static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_idx, const u8 *mac_addr, u8 key_idx, bool pairwise, const u8 *mac_addr,
struct key_params *params) struct key_params *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);
...@@ -131,6 +131,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -131,6 +131,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
if (IS_ERR(key)) if (IS_ERR(key))
return PTR_ERR(key); return PTR_ERR(key);
if (pairwise)
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
mutex_lock(&sdata->local->sta_mtx); mutex_lock(&sdata->local->sta_mtx);
if (mac_addr) { if (mac_addr) {
...@@ -153,7 +156,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -153,7 +156,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
} }
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_idx, const u8 *mac_addr) u8 key_idx, bool pairwise, const u8 *mac_addr)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct sta_info *sta; struct sta_info *sta;
...@@ -170,11 +173,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -170,11 +173,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta) if (!sta)
goto out_unlock; goto out_unlock;
if (sta->key) { if (pairwise) {
ieee80211_key_free(sdata->local, sta->key); if (sta->ptk) {
WARN_ON(sta->key); ieee80211_key_free(sdata->local, sta->ptk);
ret = 0;
}
} else {
if (sta->gtk[key_idx]) {
ieee80211_key_free(sdata->local,
sta->gtk[key_idx]);
ret = 0; ret = 0;
} }
}
goto out_unlock; goto out_unlock;
} }
...@@ -195,7 +205,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -195,7 +205,8 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
} }
static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
u8 key_idx, const u8 *mac_addr, void *cookie, u8 key_idx, bool pairwise, const u8 *mac_addr,
void *cookie,
void (*callback)(void *cookie, void (*callback)(void *cookie,
struct key_params *params)) struct key_params *params))
{ {
...@@ -203,7 +214,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -203,7 +214,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta = NULL; struct sta_info *sta = NULL;
u8 seq[6] = {0}; u8 seq[6] = {0};
struct key_params params; struct key_params params;
struct ieee80211_key *key; struct ieee80211_key *key = NULL;
u32 iv32; u32 iv32;
u16 iv16; u16 iv16;
int err = -ENOENT; int err = -ENOENT;
...@@ -217,7 +228,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, ...@@ -217,7 +228,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
if (!sta) if (!sta)
goto out; goto out;
key = sta->key; if (pairwise)
key = sta->ptk;
else if (key_idx < NUM_DEFAULT_KEYS)
key = sta->gtk[key_idx];
} else } else
key = sdata->keys[key_idx]; key = sdata->keys[key_idx];
......
...@@ -549,8 +549,6 @@ struct ieee80211_sub_if_data { ...@@ -549,8 +549,6 @@ struct ieee80211_sub_if_data {
struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX];
unsigned int fragment_next; unsigned int fragment_next;
#define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2
struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key *default_key; struct ieee80211_key *default_key;
struct ieee80211_key *default_mgmt_key; struct ieee80211_key *default_mgmt_key;
......
...@@ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ...@@ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
might_sleep(); might_sleep();
if (!key->local->ops->set_key) { if (!key->local->ops->set_key)
ret = -EOPNOTSUPP;
goto out_unsupported; goto out_unsupported;
}
assert_key_lock(key->local); assert_key_lock(key->local);
sta = get_sta_for_key(key); sta = get_sta_for_key(key);
/*
* If this is a per-STA GTK, check if it
* is supported; if not, return.
*/
if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
!(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK))
goto out_unsupported;
sdata = key->sdata; sdata = key->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss, sdata = container_of(sdata->bss,
...@@ -85,16 +91,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ...@@ -85,16 +91,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
if (!ret) if (!ret) {
key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
return 0;
}
if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) if (ret != -ENOSPC && ret != -EOPNOTSUPP)
wiphy_err(key->local->hw.wiphy, wiphy_err(key->local->hw.wiphy,
"failed to set key (%d, %pM) to hardware (%d)\n", "failed to set key (%d, %pM) to hardware (%d)\n",
key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
out_unsupported: out_unsupported:
if (ret) {
switch (key->conf.cipher) { switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_WEP104:
...@@ -102,14 +109,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ...@@ -102,14 +109,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_AES_CMAC:
/* all of these we can do in software */ /* all of these we can do in software */
ret = 0; return 0;
break;
default: default:
ret = -EINVAL; return -EINVAL;
}
} }
return ret;
} }
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
...@@ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) ...@@ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
} }
void ieee80211_key_removed(struct ieee80211_key_conf *key_conf)
{
struct ieee80211_key *key;
key = container_of(key_conf, struct ieee80211_key, conf);
might_sleep();
assert_key_lock(key->local);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
/*
* Flush TX path to avoid attempts to use this key
* after this function returns. Until then, drivers
* must be prepared to handle the key.
*/
synchronize_rcu();
}
EXPORT_SYMBOL_GPL(ieee80211_key_removed);
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
int idx) int idx)
{ {
...@@ -202,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, ...@@ -202,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sta_info *sta,
bool pairwise,
struct ieee80211_key *old, struct ieee80211_key *old,
struct ieee80211_key *new) struct ieee80211_key *new)
{ {
...@@ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, ...@@ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
if (new) if (new)
list_add(&new->list, &sdata->key_list); list_add(&new->list, &sdata->key_list);
if (sta) { if (sta && pairwise) {
rcu_assign_pointer(sta->key, new); rcu_assign_pointer(sta->ptk, new);
} else if (sta) {
if (old)
idx = old->conf.keyidx;
else
idx = new->conf.keyidx;
rcu_assign_pointer(sta->gtk[idx], new);
} else { } else {
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
...@@ -355,6 +385,7 @@ int ieee80211_key_link(struct ieee80211_key *key, ...@@ -355,6 +385,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
{ {
struct ieee80211_key *old_key; struct ieee80211_key *old_key;
int idx, ret; int idx, ret;
bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
BUG_ON(!sdata); BUG_ON(!sdata);
BUG_ON(!key); BUG_ON(!key);
...@@ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key, ...@@ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
*/ */
if (test_sta_flags(sta, WLAN_STA_WME)) if (test_sta_flags(sta, WLAN_STA_WME))
key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
/*
* This key is for a specific sta interface,
* inform the driver that it should try to store
* this key as pairwise key.
*/
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
} else { } else {
if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->vif.type == NL80211_IFTYPE_STATION) {
struct sta_info *ap; struct sta_info *ap;
...@@ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key, ...@@ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key,
mutex_lock(&sdata->local->key_mtx); mutex_lock(&sdata->local->key_mtx);
if (sta) if (sta && pairwise)
old_key = sta->key; old_key = sta->ptk;
else if (sta)
old_key = sta->gtk[idx];
else else
old_key = sdata->keys[idx]; old_key = sdata->keys[idx];
__ieee80211_key_replace(sdata, sta, old_key, key); __ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
__ieee80211_key_destroy(old_key); __ieee80211_key_destroy(old_key);
ieee80211_debugfs_key_add(key); ieee80211_debugfs_key_add(key);
...@@ -423,6 +449,7 @@ static void __ieee80211_key_free(struct ieee80211_key *key) ...@@ -423,6 +449,7 @@ static void __ieee80211_key_free(struct ieee80211_key *key)
*/ */
if (key->sdata) if (key->sdata)
__ieee80211_key_replace(key->sdata, key->sta, __ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL); key, NULL);
__ieee80211_key_destroy(key); __ieee80211_key_destroy(key);
} }
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2
#define WEP_IV_LEN 4 #define WEP_IV_LEN 4
#define WEP_ICV_LEN 4 #define WEP_ICV_LEN 4
#define ALG_TKIP_KEY_LEN 32 #define ALG_TKIP_KEY_LEN 32
......
...@@ -846,7 +846,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) ...@@ -846,7 +846,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
int keyidx; int keyidx;
int hdrlen; int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE; ieee80211_rx_result result = RX_DROP_UNUSABLE;
struct ieee80211_key *stakey = NULL; struct ieee80211_key *sta_ptk = NULL;
int mmie_keyidx = -1; int mmie_keyidx = -1;
__le16 fc; __le16 fc;
...@@ -888,15 +888,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) ...@@ -888,15 +888,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
rx->key = NULL; rx->key = NULL;
if (rx->sta) if (rx->sta)
stakey = rcu_dereference(rx->sta->key); sta_ptk = rcu_dereference(rx->sta->ptk);
fc = hdr->frame_control; fc = hdr->frame_control;
if (!ieee80211_has_protected(fc)) if (!ieee80211_has_protected(fc))
mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
if (!is_multicast_ether_addr(hdr->addr1) && stakey) { if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
rx->key = stakey; rx->key = sta_ptk;
if ((status->flag & RX_FLAG_DECRYPTED) && if ((status->flag & RX_FLAG_DECRYPTED) &&
(status->flag & RX_FLAG_IV_STRIPPED)) (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE; return RX_CONTINUE;
...@@ -912,6 +912,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) ...@@ -912,6 +912,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS || if (mmie_keyidx < NUM_DEFAULT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
return RX_DROP_MONITOR; /* unexpected BIP keyidx */ return RX_DROP_MONITOR; /* unexpected BIP keyidx */
if (rx->sta)
rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
if (!rx->key)
rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
} else if (!ieee80211_has_protected(fc)) { } else if (!ieee80211_has_protected(fc)) {
/* /*
...@@ -955,18 +958,26 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) ...@@ -955,18 +958,26 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
keyidx = keyid >> 6; keyidx = keyid >> 6;
/* check per-station GTK first, if multicast packet */
if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
/* if not found, try default key */
if (!rx->key) {
rx->key = rcu_dereference(rx->sdata->keys[keyidx]); rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
/* /*
* RSNA-protected unicast frames should always be sent with * RSNA-protected unicast frames should always be
* pairwise or station-to-station keys, but for WEP we allow * sent with pairwise or station-to-station keys,
* using a key index as well. * but for WEP we allow using a key index as well.
*/ */
if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && if (rx->key &&
rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
!is_multicast_ether_addr(hdr->addr1)) !is_multicast_ether_addr(hdr->addr1))
rx->key = NULL; rx->key = NULL;
} }
}
if (rx->key) { if (rx->key) {
rx->key->tx_rx_count++; rx->key->tx_rx_count++;
......
...@@ -616,7 +616,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) ...@@ -616,7 +616,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb; struct sk_buff *skb;
unsigned long flags; unsigned long flags;
int ret; int ret, i;
might_sleep(); might_sleep();
...@@ -644,10 +644,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) ...@@ -644,10 +644,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
if (ret) if (ret)
return ret; return ret;
if (sta->key) { for (i = 0; i < NUM_DEFAULT_KEYS; i++)
ieee80211_key_free(local, sta->key); ieee80211_key_free(local, sta->gtk[i]);
WARN_ON(sta->key); if (sta->ptk)
} ieee80211_key_free(local, sta->ptk);
sta->dead = true; sta->dead = true;
......
...@@ -199,7 +199,8 @@ enum plink_state { ...@@ -199,7 +199,8 @@ enum plink_state {
* @hnext: hash table linked list pointer * @hnext: hash table linked list pointer
* @local: pointer to the global information * @local: pointer to the global information
* @sdata: virtual interface this station belongs to * @sdata: virtual interface this station belongs to
* @key: peer key negotiated with this station, if any * @ptk: peer key negotiated with this station, if any
* @gtk: group keys negotiated with this station, if any
* @rate_ctrl: rate control algorithm reference * @rate_ctrl: rate control algorithm reference
* @rate_ctrl_priv: rate control private per-STA pointer * @rate_ctrl_priv: rate control private per-STA pointer
* @last_tx_rate: rate used for last transmit, to report to userspace as * @last_tx_rate: rate used for last transmit, to report to userspace as
...@@ -254,7 +255,8 @@ struct sta_info { ...@@ -254,7 +255,8 @@ struct sta_info {
struct sta_info *hnext; struct sta_info *hnext;
struct ieee80211_local *local; struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_key *key; struct ieee80211_key *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key *ptk;
struct rate_control_ref *rate_ctrl; struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv; void *rate_ctrl_priv;
spinlock_t lock; spinlock_t lock;
......
...@@ -532,7 +532,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) ...@@ -532,7 +532,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
tx->key = NULL; tx->key = NULL;
else if (tx->sta && (key = rcu_dereference(tx->sta->key))) else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
tx->key = key; tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) && else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) && is_multicast_ether_addr(hdr->addr1) &&
......
...@@ -375,7 +375,7 @@ bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); ...@@ -375,7 +375,7 @@ bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev);
/* internal helpers */ /* internal helpers */
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx, struct key_params *params, int key_idx,
const u8 *mac_addr); bool pairwise, const u8 *mac_addr);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap); size_t ie_len, u16 reason, bool from_ap);
void cfg80211_sme_scan_done(struct net_device *dev); void cfg80211_sme_scan_done(struct net_device *dev);
......
...@@ -160,7 +160,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) ...@@ -160,7 +160,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
*/ */
if (rdev->ops->del_key) if (rdev->ops->del_key)
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
rdev->ops->del_key(wdev->wiphy, dev, i, NULL); rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
if (wdev->current_bss) { if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss); cfg80211_unhold_bss(wdev->current_bss);
......
...@@ -93,6 +93,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -93,6 +93,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
[NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
...@@ -168,7 +169,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -168,7 +169,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
}; };
/* policy for the attributes */ /* policy for the key attributes */
static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
[NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
[NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_IDX] = { .type = NLA_U8 },
...@@ -176,6 +177,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { ...@@ -176,6 +177,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
[NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
[NL80211_KEY_TYPE] = { .type = NLA_U32 },
}; };
/* ifidx get helper */ /* ifidx get helper */
...@@ -306,6 +308,7 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, ...@@ -306,6 +308,7 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
struct key_parse { struct key_parse {
struct key_params p; struct key_params p;
int idx; int idx;
int type;
bool def, defmgmt; bool def, defmgmt;
}; };
...@@ -336,6 +339,12 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) ...@@ -336,6 +339,12 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
if (tb[NL80211_KEY_CIPHER]) if (tb[NL80211_KEY_CIPHER])
k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
if (tb[NL80211_KEY_TYPE]) {
k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
return -EINVAL;
}
return 0; return 0;
} }
...@@ -360,6 +369,12 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) ...@@ -360,6 +369,12 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
return -EINVAL;
}
return 0; return 0;
} }
...@@ -369,6 +384,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) ...@@ -369,6 +384,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
memset(k, 0, sizeof(*k)); memset(k, 0, sizeof(*k));
k->idx = -1; k->idx = -1;
k->type = -1;
if (info->attrs[NL80211_ATTR_KEY]) if (info->attrs[NL80211_ATTR_KEY])
err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
...@@ -433,7 +449,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, ...@@ -433,7 +449,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
} else if (parse.defmgmt) } else if (parse.defmgmt)
goto error; goto error;
err = cfg80211_validate_key_settings(rdev, &parse.p, err = cfg80211_validate_key_settings(rdev, &parse.p,
parse.idx, NULL); parse.idx, false, NULL);
if (err) if (err)
goto error; goto error;
result->params[parse.idx].cipher = parse.p.cipher; result->params[parse.idx].cipher = parse.p.cipher;
...@@ -516,6 +532,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -516,6 +532,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
dev->wiphy.max_scan_ie_len); dev->wiphy.max_scan_ie_len);
if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * dev->wiphy.n_cipher_suites, sizeof(u32) * dev->wiphy.n_cipher_suites,
dev->wiphy.cipher_suites); dev->wiphy.cipher_suites);
...@@ -1446,7 +1465,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1446,7 +1465,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
int err; int err;
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
u8 key_idx = 0; u8 key_idx = 0;
u8 *mac_addr = NULL; const u8 *mac_addr = NULL;
bool pairwise;
struct get_key_cookie cookie = { struct get_key_cookie cookie = {
.error = 0, .error = 0,
}; };
...@@ -1462,6 +1482,17 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1462,6 +1482,17 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC]) if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
pairwise = !!mac_addr;
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
if (kt >= NUM_NL80211_KEYTYPES)
return -EINVAL;
if (kt != NL80211_KEYTYPE_GROUP &&
kt != NL80211_KEYTYPE_PAIRWISE)
return -EINVAL;
pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
}
if (!rdev->ops->get_key) if (!rdev->ops->get_key)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -1482,8 +1513,12 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1482,8 +1513,12 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (mac_addr) if (mac_addr)
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr, if (pairwise && mac_addr &&
&cookie, get_key_callback); !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
return -ENOENT;
err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
mac_addr, &cookie, get_key_callback);
if (err) if (err)
goto free_msg; goto free_msg;
...@@ -1553,7 +1588,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1553,7 +1588,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
int err; int err;
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct key_parse key; struct key_parse key;
u8 *mac_addr = NULL; const u8 *mac_addr = NULL;
err = nl80211_parse_key(info, &key); err = nl80211_parse_key(info, &key);
if (err) if (err)
...@@ -1565,16 +1600,31 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1565,16 +1600,31 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC]) if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (key.type == -1) {
if (mac_addr)
key.type = NL80211_KEYTYPE_PAIRWISE;
else
key.type = NL80211_KEYTYPE_GROUP;
}
/* for now */
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
key.type != NL80211_KEYTYPE_GROUP)
return -EINVAL;
if (!rdev->ops->add_key) if (!rdev->ops->add_key)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr))
return -EINVAL; return -EINVAL;
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = nl80211_key_allowed(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr);
if (!err) if (!err)
err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr, &key.p); mac_addr, &key.p);
wdev_unlock(dev->ieee80211_ptr); wdev_unlock(dev->ieee80211_ptr);
...@@ -1596,13 +1646,32 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) ...@@ -1596,13 +1646,32 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC]) if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (key.type == -1) {
if (mac_addr)
key.type = NL80211_KEYTYPE_PAIRWISE;
else
key.type = NL80211_KEYTYPE_GROUP;
}
/* for now */
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
key.type != NL80211_KEYTYPE_GROUP)
return -EINVAL;
if (!rdev->ops->del_key) if (!rdev->ops->del_key)
return -EOPNOTSUPP; return -EOPNOTSUPP;
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = nl80211_key_allowed(dev->ieee80211_ptr); err = nl80211_key_allowed(dev->ieee80211_ptr);
if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
if (!err) if (!err)
err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr); err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr);
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
if (!err) { if (!err) {
...@@ -3212,6 +3281,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) ...@@ -3212,6 +3281,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
return err; return err;
if (key.idx >= 0) { if (key.idx >= 0) {
if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
return -EINVAL;
if (!key.p.key || !key.p.key_len) if (!key.p.key || !key.p.key_len)
return -EINVAL; return -EINVAL;
if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
......
...@@ -698,7 +698,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, ...@@ -698,7 +698,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
*/ */
if (rdev->ops->del_key) if (rdev->ops->del_key)
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
rdev->ops->del_key(wdev->wiphy, dev, i, NULL); rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu)); memset(&wrqu, 0, sizeof(wrqu));
......
...@@ -144,19 +144,25 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy) ...@@ -144,19 +144,25 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy)
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct key_params *params, int key_idx, struct key_params *params, int key_idx,
const u8 *mac_addr) bool pairwise, const u8 *mac_addr)
{ {
int i; int i;
if (key_idx > 5) if (key_idx > 5)
return -EINVAL; return -EINVAL;
if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
return -EINVAL;
if (pairwise && !mac_addr)
return -EINVAL;
/* /*
* Disallow pairwise keys with non-zero index unless it's WEP * Disallow pairwise keys with non-zero index unless it's WEP
* (because current deployments use pairwise WEP keys with * (because current deployments use pairwise WEP keys with
* non-zero indizes but 802.11i clearly specifies to use zero) * non-zero indizes but 802.11i clearly specifies to use zero)
*/ */
if (mac_addr && key_idx && if (pairwise && key_idx &&
params->cipher != WLAN_CIPHER_SUITE_WEP40 && params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
params->cipher != WLAN_CIPHER_SUITE_WEP104) params->cipher != WLAN_CIPHER_SUITE_WEP104)
return -EINVAL; return -EINVAL;
...@@ -677,7 +683,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) ...@@ -677,7 +683,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
if (!wdev->connect_keys->params[i].cipher) if (!wdev->connect_keys->params[i].cipher)
continue; continue;
if (rdev->ops->add_key(wdev->wiphy, dev, i, NULL, if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL,
&wdev->connect_keys->params[i])) { &wdev->connect_keys->params[i])) {
printk(KERN_ERR "%s: failed to set key %d\n", printk(KERN_ERR "%s: failed to set key %d\n",
dev->name, i); dev->name, i);
......
...@@ -432,14 +432,17 @@ int cfg80211_wext_giwretry(struct net_device *dev, ...@@ -432,14 +432,17 @@ int cfg80211_wext_giwretry(struct net_device *dev,
EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry);
static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *addr, struct net_device *dev, bool pairwise,
bool remove, bool tx_key, int idx, const u8 *addr, bool remove, bool tx_key,
struct key_params *params) int idx, struct key_params *params)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
int err, i; int err, i;
bool rejoin = false; bool rejoin = false;
if (pairwise && !addr)
return -EINVAL;
if (!wdev->wext.keys) { if (!wdev->wext.keys) {
wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
GFP_KERNEL); GFP_KERNEL);
...@@ -478,7 +481,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, ...@@ -478,7 +481,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
__cfg80211_leave_ibss(rdev, wdev->netdev, true); __cfg80211_leave_ibss(rdev, wdev->netdev, true);
rejoin = true; rejoin = true;
} }
err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr);
if (!pairwise && addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
else
err = rdev->ops->del_key(&rdev->wiphy, dev, idx,
pairwise, addr);
} }
wdev->wext.connect.privacy = false; wdev->wext.connect.privacy = false;
/* /*
...@@ -507,12 +516,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, ...@@ -507,12 +516,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (addr) if (addr)
tx_key = false; tx_key = false;
if (cfg80211_validate_key_settings(rdev, params, idx, addr)) if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr))
return -EINVAL; return -EINVAL;
err = 0; err = 0;
if (wdev->current_bss) if (wdev->current_bss)
err = rdev->ops->add_key(&rdev->wiphy, dev, idx, addr, params); err = rdev->ops->add_key(&rdev->wiphy, dev, idx,
pairwise, addr, params);
if (err) if (err)
return err; return err;
...@@ -563,17 +573,17 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, ...@@ -563,17 +573,17 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
} }
static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *addr, struct net_device *dev, bool pairwise,
bool remove, bool tx_key, int idx, const u8 *addr, bool remove, bool tx_key,
struct key_params *params) int idx, struct key_params *params)
{ {
int err; int err;
/* devlist mutex needed for possible IBSS re-join */ /* devlist mutex needed for possible IBSS re-join */
mutex_lock(&rdev->devlist_mtx); mutex_lock(&rdev->devlist_mtx);
wdev_lock(dev->ieee80211_ptr); wdev_lock(dev->ieee80211_ptr);
err = __cfg80211_set_encryption(rdev, dev, addr, remove, err = __cfg80211_set_encryption(rdev, dev, pairwise, addr,
tx_key, idx, params); remove, tx_key, idx, params);
wdev_unlock(dev->ieee80211_ptr); wdev_unlock(dev->ieee80211_ptr);
mutex_unlock(&rdev->devlist_mtx); mutex_unlock(&rdev->devlist_mtx);
...@@ -635,7 +645,7 @@ int cfg80211_wext_siwencode(struct net_device *dev, ...@@ -635,7 +645,7 @@ int cfg80211_wext_siwencode(struct net_device *dev,
else if (!remove) else if (!remove)
return -EINVAL; return -EINVAL;
return cfg80211_set_encryption(rdev, dev, NULL, remove, return cfg80211_set_encryption(rdev, dev, false, NULL, remove,
wdev->wext.default_key == -1, wdev->wext.default_key == -1,
idx, &params); idx, &params);
} }
...@@ -725,7 +735,9 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, ...@@ -725,7 +735,9 @@ int cfg80211_wext_siwencodeext(struct net_device *dev,
} }
return cfg80211_set_encryption( return cfg80211_set_encryption(
rdev, dev, addr, remove, rdev, dev,
!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY),
addr, remove,
ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
idx, &params); idx, &params);
} }
......
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