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: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
		
					parent
					
						
							
								53f73c09d6
							
						
					
				
			
			
				commit
				
					
						e31b82136d
					
				
			
		
					 20 changed files with 293 additions and 109 deletions
				
			
		|  | @ -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, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 	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,8 +2308,8 @@ 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); | ||||||
| 	struct usbnet *usbdev = priv->usbdev; | 	struct usbnet *usbdev = priv->usbdev; | ||||||
|  | @ -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 { | ||||||
|  *      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 { | ||||||
| 	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 { | ||||||
|  * @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 { | ||||||
|  *	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 { | ||||||
| 	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 { | ||||||
| 				       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 { | ||||||
|  * @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 { | ||||||
| 	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 { | ||||||
|  * @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 { | ||||||
| 	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); | ||||||
| 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, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 	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, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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,10 +173,17 @@ 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; | 				ret = 0; | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if (sta->gtk[key_idx]) { | ||||||
|  | 				ieee80211_key_free(sdata->local, | ||||||
|  | 						   sta->gtk[key_idx]); | ||||||
|  | 				ret = 0; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		goto out_unlock; | 		goto out_unlock; | ||||||
|  | @ -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, | ||||||
| 	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, | ||||||
| 		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 { | ||||||
| 	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) | ||||||
| 
 | 
 | ||||||
| 	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,31 +91,28 @@ 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: | 	case WLAN_CIPHER_SUITE_TKIP: | ||||||
| 		case WLAN_CIPHER_SUITE_TKIP: | 	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 */ | 		return 0; | ||||||
| 			ret = 0; | 	default: | ||||||
| 			break; | 		return -EINVAL; | ||||||
| 		default: |  | ||||||
| 			ret = -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) | ||||||
| 	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, | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 	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, | ||||||
| { | { | ||||||
| 	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, | ||||||
| 		 */ | 		 */ | ||||||
| 		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, | ||||||
| 
 | 
 | ||||||
| 	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,7 +449,8 @@ 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, NULL); | 				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, | ||||||
|  | 				key, NULL); | ||||||
| 	__ieee80211_key_destroy(key); | 	__ieee80211_key_destroy(key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 	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) | ||||||
| 	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,7 +912,10 @@ 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 */ | ||||||
| 		rx->key = rcu_dereference(rx->sdata->keys[mmie_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]); | ||||||
| 	} else if (!ieee80211_has_protected(fc)) { | 	} else if (!ieee80211_has_protected(fc)) { | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * The frame was not protected, so skip decryption. However, we | 		 * The frame was not protected, so skip decryption. However, we | ||||||
|  | @ -955,17 +958,25 @@ 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; | ||||||
| 
 | 
 | ||||||
| 		rx->key = rcu_dereference(rx->sdata->keys[keyidx]); | 		/* 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 */ | ||||||
| 		 * RSNA-protected unicast frames should always be sent with | 		if (!rx->key) { | ||||||
| 		 * pairwise or station-to-station keys, but for WEP we allow | 			rx->key = rcu_dereference(rx->sdata->keys[keyidx]); | ||||||
| 		 * using a key index as well. | 
 | ||||||
| 		 */ | 			/*
 | ||||||
| 		if (rx->key && rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && | 			 * RSNA-protected unicast frames should always be | ||||||
| 		    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && | 			 * sent with pairwise or station-to-station keys, | ||||||
| 		    !is_multicast_ether_addr(hdr->addr1)) | 			 * but for WEP we allow using a key index as well. | ||||||
| 			rx->key = NULL; | 			 */ | ||||||
|  | 			if (rx->key && | ||||||
|  | 			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 && | ||||||
|  | 			    rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 && | ||||||
|  | 			    !is_multicast_ether_addr(hdr->addr1)) | ||||||
|  | 				rx->key = NULL; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (rx->key) { | 	if (rx->key) { | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 	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 { | ||||||
|  * @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 { | ||||||
| 	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) | ||||||
| 
 | 
 | ||||||
| 	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); | ||||||
| /* 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) | ||||||
| 	 */ | 	 */ | ||||||
| 	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] = { | ||||||
| 	[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] = { | ||||||
| 	[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] = { | ||||||
| 	[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, | ||||||
| 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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 
 | 
 | ||||||
| 	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, | ||||||
| 		} 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, | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 	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) | ||||||
| 		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, | ||||||
| 	 */ | 	 */ | ||||||
| 	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) | ||||||
| 
 | 
 | ||||||
| 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) | ||||||
| 	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, | ||||||
| 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, | ||||||
| 				__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, | ||||||
| 	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, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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, | ||||||
| 	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, ¶ms); | 				       idx, ¶ms); | ||||||
| } | } | ||||||
|  | @ -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, ¶ms); | 			idx, ¶ms); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Johannes Berg
				Johannes Berg