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, | ||||
| 				u8 key_index, const u8 *mac_addr, | ||||
| 				u8 key_index, bool pairwise, const u8 *mac_addr, | ||||
| 				struct key_params *params) | ||||
| { | ||||
| 	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, | ||||
| 				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*)) | ||||
| { | ||||
|  | @ -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, | ||||
| 				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_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, | ||||
| 			   u8 idx, const u8 *mac_addr, | ||||
| 			   u8 idx, bool pairwise, const u8 *mac_addr, | ||||
| 			   struct key_params *params) | ||||
| { | ||||
| 	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, | ||||
| 			   u8 key_index, const u8 *mac_addr) | ||||
| 			   u8 key_index, bool pairwise, const u8 *mac_addr) | ||||
| { | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| 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, | ||||
| 								u8 key_index); | ||||
|  | @ -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, | ||||
| 					u8 key_index, const u8 *mac_addr, | ||||
| 			 u8 key_index, bool pairwise, const u8 *mac_addr, | ||||
| 			 struct key_params *params) | ||||
| { | ||||
| 	struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||||
|  | @ -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, | ||||
| 					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 usbnet *usbdev = priv->usbdev; | ||||
|  |  | |||
|  | @ -801,6 +801,9 @@ enum nl80211_commands { | |||
|  *      This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING | ||||
|  *      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_AFTER_LAST: internal use | ||||
|  */ | ||||
|  | @ -968,6 +971,8 @@ enum nl80211_attrs { | |||
| 	NL80211_ATTR_CONTROL_PORT_ETHERTYPE, | ||||
| 	NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, | ||||
| 
 | ||||
| 	NL80211_ATTR_SUPPORT_IBSS_RSN, | ||||
| 
 | ||||
| 	/* add attributes here, update the policy in nl80211.c */ | ||||
| 
 | ||||
| 	__NL80211_ATTR_AFTER_LAST, | ||||
|  | @ -1659,11 +1664,14 @@ enum nl80211_auth_type { | |||
|  * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key | ||||
|  * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key | ||||
|  * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) | ||||
|  * @NUM_NL80211_KEYTYPES: number of defined key types | ||||
|  */ | ||||
| enum nl80211_key_type { | ||||
| 	NL80211_KEYTYPE_GROUP, | ||||
| 	NL80211_KEYTYPE_PAIRWISE, | ||||
| 	NL80211_KEYTYPE_PEERKEY, | ||||
| 
 | ||||
| 	NUM_NL80211_KEYTYPES | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -1694,6 +1702,9 @@ enum nl80211_wpa_versions { | |||
|  *	CCMP keys, each six bytes in little endian | ||||
|  * @NL80211_KEY_DEFAULT: flag indicating default 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_MAX: highest key attribute | ||||
|  */ | ||||
|  | @ -1705,6 +1716,7 @@ enum nl80211_key_attributes { | |||
| 	NL80211_KEY_SEQ, | ||||
| 	NL80211_KEY_DEFAULT, | ||||
| 	NL80211_KEY_DEFAULT_MGMT, | ||||
| 	NL80211_KEY_TYPE, | ||||
| 
 | ||||
| 	/* keep last */ | ||||
| 	__NL80211_KEY_AFTER_LAST, | ||||
|  |  | |||
|  | @ -1130,13 +1130,14 @@ struct cfg80211_ops { | |||
| 				       struct vif_params *params); | ||||
| 
 | ||||
| 	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); | ||||
| 	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*)); | ||||
| 	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, | ||||
| 				   struct net_device *netdev, | ||||
| 				   u8 key_index); | ||||
|  | @ -1304,6 +1305,7 @@ struct cfg80211_ops { | |||
|  * @WIPHY_FLAG_CONTROL_PORT_PROTOCOL: This device supports setting the | ||||
|  *	control port protocol ethertype. The device also honours the | ||||
|  *	control_port_no_encrypt flag. | ||||
|  * @WIPHY_FLAG_IBSS_RSN: The device supports IBSS RSN. | ||||
|  */ | ||||
| enum wiphy_flags { | ||||
| 	WIPHY_FLAG_CUSTOM_REGULATORY		= BIT(0), | ||||
|  | @ -1314,6 +1316,7 @@ enum wiphy_flags { | |||
| 	WIPHY_FLAG_4ADDR_AP			= BIT(5), | ||||
| 	WIPHY_FLAG_4ADDR_STATION		= BIT(6), | ||||
| 	WIPHY_FLAG_CONTROL_PORT_PROTOCOL	= BIT(7), | ||||
| 	WIPHY_FLAG_IBSS_RSN			= BIT(7), | ||||
| }; | ||||
| 
 | ||||
| struct mac_address { | ||||
|  |  | |||
|  | @ -1041,6 +1041,13 @@ enum ieee80211_tkip_key_type { | |||
|  * @IEEE80211_HW_NEED_DTIM_PERIOD: | ||||
|  *	This device needs to know the DTIM period for the BSS before | ||||
|  *	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 { | ||||
| 	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_CONNECTION_MONITOR			= 1<<19, | ||||
| 	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, | ||||
| 			    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 */ | ||||
| 
 | ||||
| /**
 | ||||
|  |  | |||
|  | @ -103,7 +103,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, | |||
| } | ||||
| 
 | ||||
| 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 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)) | ||||
| 		return PTR_ERR(key); | ||||
| 
 | ||||
| 	if (pairwise) | ||||
| 		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; | ||||
| 
 | ||||
| 	mutex_lock(&sdata->local->sta_mtx); | ||||
| 
 | ||||
| 	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, | ||||
| 			     u8 key_idx, const u8 *mac_addr) | ||||
| 			     u8 key_idx, bool pairwise, const u8 *mac_addr) | ||||
| { | ||||
| 	struct ieee80211_sub_if_data *sdata; | ||||
| 	struct sta_info *sta; | ||||
|  | @ -170,11 +173,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, | |||
| 		if (!sta) | ||||
| 			goto out_unlock; | ||||
| 
 | ||||
| 		if (sta->key) { | ||||
| 			ieee80211_key_free(sdata->local, sta->key); | ||||
| 			WARN_ON(sta->key); | ||||
| 		if (pairwise) { | ||||
| 			if (sta->ptk) { | ||||
| 				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; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		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, | ||||
| 			     u8 key_idx, const u8 *mac_addr, void *cookie, | ||||
| 			     u8 key_idx, bool pairwise, const u8 *mac_addr, | ||||
| 			     void *cookie, | ||||
| 			     void (*callback)(void *cookie, | ||||
| 					      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; | ||||
| 	u8 seq[6] = {0}; | ||||
| 	struct key_params params; | ||||
| 	struct ieee80211_key *key; | ||||
| 	struct ieee80211_key *key = NULL; | ||||
| 	u32 iv32; | ||||
| 	u16 iv16; | ||||
| 	int err = -ENOENT; | ||||
|  | @ -217,7 +228,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
| 		if (!sta) | ||||
| 			goto out; | ||||
| 
 | ||||
| 		key = sta->key; | ||||
| 		if (pairwise) | ||||
| 			key = sta->ptk; | ||||
| 		else if (key_idx < NUM_DEFAULT_KEYS) | ||||
| 			key = sta->gtk[key_idx]; | ||||
| 	} else | ||||
| 		key = sdata->keys[key_idx]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -549,8 +549,6 @@ struct ieee80211_sub_if_data { | |||
| 	struct ieee80211_fragment_entry	fragments[IEEE80211_FRAGMENT_MAX]; | ||||
| 	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 *default_key; | ||||
| 	struct ieee80211_key *default_mgmt_key; | ||||
|  |  | |||
|  | @ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
| 
 | ||||
| 	might_sleep(); | ||||
| 
 | ||||
| 	if (!key->local->ops->set_key) { | ||||
| 		ret = -EOPNOTSUPP; | ||||
| 	if (!key->local->ops->set_key) | ||||
| 		goto out_unsupported; | ||||
| 	} | ||||
| 
 | ||||
| 	assert_key_lock(key->local); | ||||
| 
 | ||||
| 	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; | ||||
| 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||||
| 		sdata = container_of(sdata->bss, | ||||
|  | @ -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); | ||||
| 
 | ||||
| 	if (!ret) | ||||
| 	if (!ret) { | ||||
| 		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, | ||||
| 			  "failed to set key (%d, %pM) to hardware (%d)\n", | ||||
| 			  key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); | ||||
| 
 | ||||
|  out_unsupported: | ||||
| 	if (ret) { | ||||
| 	switch (key->conf.cipher) { | ||||
| 	case WLAN_CIPHER_SUITE_WEP40: | ||||
| 	case WLAN_CIPHER_SUITE_WEP104: | ||||
|  | @ -102,16 +109,12 @@ out_unsupported: | |||
| 	case WLAN_CIPHER_SUITE_CCMP: | ||||
| 	case WLAN_CIPHER_SUITE_AES_CMAC: | ||||
| 		/* all of these we can do in software */ | ||||
| 			ret = 0; | ||||
| 			break; | ||||
| 		return 0; | ||||
| 	default: | ||||
| 			ret = -EINVAL; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | ||||
| { | ||||
| 	struct ieee80211_sub_if_data *sdata; | ||||
|  | @ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
| 	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, | ||||
| 					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, | ||||
| 				    struct sta_info *sta, | ||||
| 				    bool pairwise, | ||||
| 				    struct ieee80211_key *old, | ||||
| 				    struct ieee80211_key *new) | ||||
| { | ||||
|  | @ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
| 	if (new) | ||||
| 		list_add(&new->list, &sdata->key_list); | ||||
| 
 | ||||
| 	if (sta) { | ||||
| 		rcu_assign_pointer(sta->key, new); | ||||
| 	if (sta && pairwise) { | ||||
| 		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 { | ||||
| 		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; | ||||
| 	int idx, ret; | ||||
| 	bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; | ||||
| 
 | ||||
| 	BUG_ON(!sdata); | ||||
| 	BUG_ON(!key); | ||||
|  | @ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
| 		 */ | ||||
| 		if (test_sta_flags(sta, WLAN_STA_WME)) | ||||
| 			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 { | ||||
| 		if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||||
| 			struct sta_info *ap; | ||||
|  | @ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
| 
 | ||||
| 	mutex_lock(&sdata->local->key_mtx); | ||||
| 
 | ||||
| 	if (sta) | ||||
| 		old_key = sta->key; | ||||
| 	if (sta && pairwise) | ||||
| 		old_key = sta->ptk; | ||||
| 	else if (sta) | ||||
| 		old_key = sta->gtk[idx]; | ||||
| 	else | ||||
| 		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_debugfs_key_add(key); | ||||
|  | @ -423,6 +449,7 @@ static void __ieee80211_key_free(struct ieee80211_key *key) | |||
| 	 */ | ||||
| 	if (key->sdata) | ||||
| 		__ieee80211_key_replace(key->sdata, key->sta, | ||||
| 				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, | ||||
| 				key, NULL); | ||||
| 	__ieee80211_key_destroy(key); | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,9 @@ | |||
| #include <linux/rcupdate.h> | ||||
| #include <net/mac80211.h> | ||||
| 
 | ||||
| #define NUM_DEFAULT_KEYS 4 | ||||
| #define NUM_DEFAULT_MGMT_KEYS 2 | ||||
| 
 | ||||
| #define WEP_IV_LEN		4 | ||||
| #define WEP_ICV_LEN		4 | ||||
| #define ALG_TKIP_KEY_LEN	32 | ||||
|  |  | |||
|  | @ -846,7 +846,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
| 	int keyidx; | ||||
| 	int hdrlen; | ||||
| 	ieee80211_rx_result result = RX_DROP_UNUSABLE; | ||||
| 	struct ieee80211_key *stakey = NULL; | ||||
| 	struct ieee80211_key *sta_ptk = NULL; | ||||
| 	int mmie_keyidx = -1; | ||||
| 	__le16 fc; | ||||
| 
 | ||||
|  | @ -888,15 +888,15 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
| 	rx->key = NULL; | ||||
| 
 | ||||
| 	if (rx->sta) | ||||
| 		stakey = rcu_dereference(rx->sta->key); | ||||
| 		sta_ptk = rcu_dereference(rx->sta->ptk); | ||||
| 
 | ||||
| 	fc = hdr->frame_control; | ||||
| 
 | ||||
| 	if (!ieee80211_has_protected(fc)) | ||||
| 		mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); | ||||
| 
 | ||||
| 	if (!is_multicast_ether_addr(hdr->addr1) && stakey) { | ||||
| 		rx->key = stakey; | ||||
| 	if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) { | ||||
| 		rx->key = sta_ptk; | ||||
| 		if ((status->flag & RX_FLAG_DECRYPTED) && | ||||
| 		    (status->flag & RX_FLAG_IV_STRIPPED)) | ||||
| 			return RX_CONTINUE; | ||||
|  | @ -912,6 +912,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
| 		if (mmie_keyidx < NUM_DEFAULT_KEYS || | ||||
| 		    mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) | ||||
| 			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]); | ||||
| 	} else if (!ieee80211_has_protected(fc)) { | ||||
| 		/*
 | ||||
|  | @ -955,18 +958,26 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
| 		skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1); | ||||
| 		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]); | ||||
| 
 | ||||
| 			/*
 | ||||
| 		 * RSNA-protected unicast frames should always be sent with | ||||
| 		 * pairwise or station-to-station keys, but for WEP we allow | ||||
| 		 * using a key index as well. | ||||
| 			 * RSNA-protected unicast frames should always be | ||||
| 			 * sent with pairwise or station-to-station keys, | ||||
| 			 * 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 && | ||||
| 			    !is_multicast_ether_addr(hdr->addr1)) | ||||
| 				rx->key = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (rx->key) { | ||||
| 		rx->key->tx_rx_count++; | ||||
|  |  | |||
|  | @ -616,7 +616,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) | |||
| 	struct ieee80211_sub_if_data *sdata; | ||||
| 	struct sk_buff *skb; | ||||
| 	unsigned long flags; | ||||
| 	int ret; | ||||
| 	int ret, i; | ||||
| 
 | ||||
| 	might_sleep(); | ||||
| 
 | ||||
|  | @ -644,10 +644,10 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) | |||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (sta->key) { | ||||
| 		ieee80211_key_free(local, sta->key); | ||||
| 		WARN_ON(sta->key); | ||||
| 	} | ||||
| 	for (i = 0; i < NUM_DEFAULT_KEYS; i++) | ||||
| 		ieee80211_key_free(local, sta->gtk[i]); | ||||
| 	if (sta->ptk) | ||||
| 		ieee80211_key_free(local, sta->ptk); | ||||
| 
 | ||||
| 	sta->dead = true; | ||||
| 
 | ||||
|  |  | |||
|  | @ -199,7 +199,8 @@ enum plink_state { | |||
|  * @hnext: hash table linked list pointer | ||||
|  * @local: pointer to the global information | ||||
|  * @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_priv: rate control private per-STA pointer | ||||
|  * @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 ieee80211_local *local; | ||||
| 	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; | ||||
| 	void *rate_ctrl_priv; | ||||
| 	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)) | ||||
| 		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; | ||||
| 	else if (ieee80211_is_mgmt(hdr->frame_control) && | ||||
| 		 is_multicast_ether_addr(hdr->addr1) && | ||||
|  |  | |||
|  | @ -375,7 +375,7 @@ bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); | |||
| /* internal helpers */ | ||||
| int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, | ||||
| 				   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, | ||||
| 			     size_t ie_len, u16 reason, bool from_ap); | ||||
| 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) | ||||
| 		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) { | ||||
| 		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_DEFAULT] = { .type = NLA_FLAG }, | ||||
| 	[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_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 }, | ||||
| }; | ||||
| 
 | ||||
| /* policy for the attributes */ | ||||
| /* policy for the key attributes */ | ||||
| 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_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_DEFAULT] = { .type = NLA_FLAG }, | ||||
| 	[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, | ||||
| 	[NL80211_KEY_TYPE] = { .type = NLA_U32 }, | ||||
| }; | ||||
| 
 | ||||
| /* ifidx get helper */ | ||||
|  | @ -306,6 +308,7 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, | |||
| struct key_parse { | ||||
| 	struct key_params p; | ||||
| 	int idx; | ||||
| 	int type; | ||||
| 	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]) | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
|  | @ -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->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; | ||||
| } | ||||
| 
 | ||||
|  | @ -369,6 +384,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) | |||
| 
 | ||||
| 	memset(k, 0, sizeof(*k)); | ||||
| 	k->idx = -1; | ||||
| 	k->type = -1; | ||||
| 
 | ||||
| 	if (info->attrs[NL80211_ATTR_KEY]) | ||||
| 		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) | ||||
| 			goto error; | ||||
| 		err = cfg80211_validate_key_settings(rdev, &parse.p, | ||||
| 						     parse.idx, NULL); | ||||
| 						     parse.idx, false, NULL); | ||||
| 		if (err) | ||||
| 			goto error; | ||||
| 		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, | ||||
| 		    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, | ||||
| 		sizeof(u32) * dev->wiphy.n_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; | ||||
| 	struct net_device *dev = info->user_ptr[1]; | ||||
| 	u8 key_idx = 0; | ||||
| 	u8 *mac_addr = NULL; | ||||
| 	const u8 *mac_addr = NULL; | ||||
| 	bool pairwise; | ||||
| 	struct get_key_cookie cookie = { | ||||
| 		.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]) | ||||
| 		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) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
|  | @ -1482,8 +1513,12 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | |||
| 	if (mac_addr) | ||||
| 		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | ||||
| 
 | ||||
| 	err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr, | ||||
| 				&cookie, get_key_callback); | ||||
| 	if (pairwise && mac_addr && | ||||
| 	    !(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) | ||||
| 		goto free_msg; | ||||
|  | @ -1553,7 +1588,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
| 	int err; | ||||
| 	struct net_device *dev = info->user_ptr[1]; | ||||
| 	struct key_parse key; | ||||
| 	u8 *mac_addr = NULL; | ||||
| 	const u8 *mac_addr = NULL; | ||||
| 
 | ||||
| 	err = nl80211_parse_key(info, &key); | ||||
| 	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]) | ||||
| 		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) | ||||
| 		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; | ||||
| 
 | ||||
| 	wdev_lock(dev->ieee80211_ptr); | ||||
| 	err = nl80211_key_allowed(dev->ieee80211_ptr); | ||||
| 	if (!err) | ||||
| 		err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, | ||||
| 					 key.type == NL80211_KEYTYPE_PAIRWISE, | ||||
| 					 mac_addr, &key.p); | ||||
| 	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]) | ||||
| 		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) | ||||
| 		return -EOPNOTSUPP; | ||||
| 
 | ||||
| 	wdev_lock(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) | ||||
| 		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 | ||||
| 	if (!err) { | ||||
|  | @ -3212,6 +3281,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
| 		return err; | ||||
| 
 | ||||
| 	if (key.idx >= 0) { | ||||
| 		if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) | ||||
| 			return -EINVAL; | ||||
| 		if (!key.p.key || !key.p.key_len) | ||||
| 			return -EINVAL; | ||||
| 		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) | ||||
| 		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 | ||||
| 	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, | ||||
| 				   struct key_params *params, int key_idx, | ||||
| 				   const u8 *mac_addr) | ||||
| 				   bool pairwise, const u8 *mac_addr) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (key_idx > 5) | ||||
| 		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 | ||||
| 	 * (because current deployments use pairwise WEP keys with | ||||
| 	 * 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_WEP104) | ||||
| 		return -EINVAL; | ||||
|  | @ -677,7 +683,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) | |||
| 	for (i = 0; i < 6; i++) { | ||||
| 		if (!wdev->connect_keys->params[i].cipher) | ||||
| 			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])) { | ||||
| 			printk(KERN_ERR "%s: failed to set key %d\n", | ||||
| 				dev->name, i); | ||||
|  |  | |||
|  | @ -432,14 +432,17 @@ int cfg80211_wext_giwretry(struct net_device *dev, | |||
| EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); | ||||
| 
 | ||||
| static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, | ||||
| 				     struct net_device *dev, const u8 *addr, | ||||
| 				     bool remove, bool tx_key, int idx, | ||||
| 				     struct key_params *params) | ||||
| 				     struct net_device *dev, bool pairwise, | ||||
| 				     const u8 *addr, bool remove, bool tx_key, | ||||
| 				     int idx, struct key_params *params) | ||||
| { | ||||
| 	struct wireless_dev *wdev = dev->ieee80211_ptr; | ||||
| 	int err, i; | ||||
| 	bool rejoin = false; | ||||
| 
 | ||||
| 	if (pairwise && !addr) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!wdev->wext.keys) { | ||||
| 		wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), | ||||
| 					      GFP_KERNEL); | ||||
|  | @ -478,7 +481,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, | |||
| 				__cfg80211_leave_ibss(rdev, wdev->netdev, 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; | ||||
| 		/*
 | ||||
|  | @ -507,12 +516,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, | |||
| 	if (addr) | ||||
| 		tx_key = false; | ||||
| 
 | ||||
| 	if (cfg80211_validate_key_settings(rdev, params, idx, addr)) | ||||
| 	if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	err = 0; | ||||
| 	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) | ||||
| 		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, | ||||
| 				   struct net_device *dev, const u8 *addr, | ||||
| 				   bool remove, bool tx_key, int idx, | ||||
| 				   struct key_params *params) | ||||
| 				   struct net_device *dev, bool pairwise, | ||||
| 				   const u8 *addr, bool remove, bool tx_key, | ||||
| 				   int idx, struct key_params *params) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	/* devlist mutex needed for possible IBSS re-join */ | ||||
| 	mutex_lock(&rdev->devlist_mtx); | ||||
| 	wdev_lock(dev->ieee80211_ptr); | ||||
| 	err = __cfg80211_set_encryption(rdev, dev, addr, remove, | ||||
| 					tx_key, idx, params); | ||||
| 	err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, | ||||
| 					remove, tx_key, idx, params); | ||||
| 	wdev_unlock(dev->ieee80211_ptr); | ||||
| 	mutex_unlock(&rdev->devlist_mtx); | ||||
| 
 | ||||
|  | @ -635,7 +645,7 @@ int cfg80211_wext_siwencode(struct net_device *dev, | |||
| 	else if (!remove) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return cfg80211_set_encryption(rdev, dev, NULL, remove, | ||||
| 	return cfg80211_set_encryption(rdev, dev, false, NULL, remove, | ||||
| 				       wdev->wext.default_key == -1, | ||||
| 				       idx, ¶ms); | ||||
| } | ||||
|  | @ -725,7 +735,9 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, | |||
| 	} | ||||
| 
 | ||||
| 	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, | ||||
| 			idx, ¶ms); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Johannes Berg
				Johannes Berg