Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96fc6efb authored by Alexander Wetzel's avatar Alexander Wetzel Committed by Johannes Berg
Browse files

mac80211: IEEE 802.11 Extended Key ID support



Add support for Extended Key ID as defined in IEEE 802.11-2016.

 - Implement the nl80211 API for Extended Key ID
 - Extend mac80211 API to allow drivers to support Extended Key ID
 - Enable Extended Key ID by default for drivers only supporting SW
   crypto (e.g. mac80211_hwsim)
 - Allow unicast Tx usage to be supressed (IEEE80211_KEY_FLAG_NO_AUTO_TX)
 - Select the decryption key based on the MPDU keyid
 - Enforce existing assumptions in the code that rekeys don't change the
   cipher

Signed-off-by: default avatarAlexander Wetzel <alexander@wetzel-home.de>
[remove module parameter]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6cdd3979
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1697,6 +1697,7 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
 * @IEEE80211_KEY_FLAG_PUT_MIC_SPACE: This flag should be set by the driver for
 *	a TKIP key if it only requires MIC space. Do not set together with
 *	@IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key.
 * @IEEE80211_KEY_FLAG_NO_AUTO_TX: Key needs explicit Tx activation.
 */
enum ieee80211_key_flags {
	IEEE80211_KEY_FLAG_GENERATE_IV_MGMT	= BIT(0),
@@ -1708,6 +1709,7 @@ enum ieee80211_key_flags {
	IEEE80211_KEY_FLAG_RX_MGMT		= BIT(6),
	IEEE80211_KEY_FLAG_RESERVE_TAILROOM	= BIT(7),
	IEEE80211_KEY_FLAG_PUT_MIC_SPACE	= BIT(8),
	IEEE80211_KEY_FLAG_NO_AUTO_TX		= BIT(9),
};

/**
@@ -2243,6 +2245,9 @@ struct ieee80211_txq {
 * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
 *	only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
 *
 * @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended
 *	Key ID and can handle two unicast keys per station for Rx and Tx.
 *
 * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
 */
enum ieee80211_hw_flags {
@@ -2294,6 +2299,7 @@ enum ieee80211_hw_flags {
	IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
	IEEE80211_HW_SUPPORTS_MULTI_BSSID,
	IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
	IEEE80211_HW_EXT_KEY_ID_NATIVE,

	/* keep last, obviously */
	NUM_IEEE80211_HW_FLAGS
+36 −0
Original line number Diff line number Diff line
@@ -351,6 +351,36 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
	return 0;
}

static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
			    const u8 *mac_addr, u8 key_idx)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_key *key;
	struct sta_info *sta;
	int ret = -EINVAL;

	if (!wiphy_ext_feature_isset(local->hw.wiphy,
				     NL80211_EXT_FEATURE_EXT_KEY_ID))
		return -EINVAL;

	sta = sta_info_get_bss(sdata, mac_addr);

	if (!sta)
		return -EINVAL;

	if (sta->ptk_idx == key_idx)
		return 0;

	mutex_lock(&local->key_mtx);
	key = key_mtx_dereference(local, sta->ptk[key_idx]);

	if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)
		ret = ieee80211_set_tx_key(key);

	mutex_unlock(&local->key_mtx);
	return ret;
}

static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
			     u8 key_idx, bool pairwise, const u8 *mac_addr,
			     struct key_params *params)
@@ -365,6 +395,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
	if (!ieee80211_sdata_running(sdata))
		return -ENETDOWN;

	if (pairwise && params->mode == NL80211_KEY_SET_TX)
		return ieee80211_set_tx(sdata, mac_addr, key_idx);

	/* reject WEP and TKIP keys if WEP failed to initialize */
	switch (params->cipher) {
	case WLAN_CIPHER_SUITE_WEP40:
@@ -396,6 +429,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
	if (pairwise)
		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;

	if (params->mode == NL80211_KEY_NO_TX)
		key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;

	mutex_lock(&local->sta_mtx);

	if (mac_addr) {
+1 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
	FLAG(TX_STATUS_NO_AMPDU_LEN),
	FLAG(SUPPORTS_MULTI_BSSID),
	FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
	FLAG(EXT_KEY_ID_NATIVE),
#undef FLAG
};

+1 −1
Original line number Diff line number Diff line
@@ -1269,7 +1269,7 @@ struct ieee80211_local {

	/*
	 * Key mutex, protects sdata's key_list and sta_info's
	 * key pointers (write access, they're RCU.)
	 * key pointers and ptk_idx (write access, they're RCU.)
	 */
	struct mutex key_mtx;

+49 −14
Original line number Diff line number Diff line
@@ -265,9 +265,24 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
			  sta ? sta->sta.addr : bcast_addr, ret);
}

int ieee80211_set_tx_key(struct ieee80211_key *key)
{
	struct sta_info *sta = key->sta;
	struct ieee80211_local *local = key->local;
	struct ieee80211_key *old;

	assert_key_lock(local);

	old = key_mtx_dereference(local, sta->ptk[sta->ptk_idx]);
	sta->ptk_idx = key->conf.keyidx;
	ieee80211_check_fast_xmit(sta);

	return 0;
}

static int ieee80211_hw_key_replace(struct ieee80211_key *old_key,
				    struct ieee80211_key *new_key,
				    bool ptk0rekey)
				    bool pairwise)
{
	struct ieee80211_sub_if_data *sdata;
	struct ieee80211_local *local;
@@ -284,8 +299,9 @@ static int ieee80211_hw_key_replace(struct ieee80211_key *old_key,
	assert_key_lock(old_key->local);
	sta = old_key->sta;

	/* PTK only using key ID 0 needs special handling on rekey */
	if (new_key && sta && ptk0rekey) {
	/* Unicast rekey without Extended Key ID needs special handling */
	if (new_key && sta && pairwise &&
	    rcu_access_pointer(sta->ptk[sta->ptk_idx]) == old_key) {
		local = old_key->local;
		sdata = old_key->sdata;

@@ -401,10 +417,6 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,

	if (old) {
		idx = old->conf.keyidx;
		/* TODO: proper implement and test "Extended Key ID for
		 * Individually Addressed Frames" from IEEE 802.11-2016.
		 * Till then always assume only key ID 0 is used for
		 * pairwise keys.*/
		ret = ieee80211_hw_key_replace(old, new, pairwise);
	} else {
		/* new must be provided in case old is not */
@@ -421,15 +433,20 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
	if (sta) {
		if (pairwise) {
			rcu_assign_pointer(sta->ptk[idx], new);
			if (new &&
			    !(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)) {
				sta->ptk_idx = idx;
			if (new) {
				clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
				ieee80211_check_fast_xmit(sta);
			}
		} else {
			rcu_assign_pointer(sta->gtk[idx], new);
		}
		if (new)
		/* Only needed for transition from no key -> key.
		 * Still triggers unnecessary when using Extended Key ID
		 * and installing the second key ID the first time.
		 */
		if (new && !old)
			ieee80211_check_fast_rx(sta);
	} else {
		defunikey = old &&
@@ -745,16 +762,34 @@ int ieee80211_key_link(struct ieee80211_key *key,
	 * can cause warnings to appear.
	 */
	bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
	int ret;
	int ret = -EOPNOTSUPP;

	mutex_lock(&sdata->local->key_mtx);

	if (sta && pairwise)
	if (sta && pairwise) {
		struct ieee80211_key *alt_key;

		old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
	else if (sta)
		alt_key = key_mtx_dereference(sdata->local, sta->ptk[idx ^ 1]);

		/* The rekey code assumes that the old and new key are using
		 * the same cipher. Enforce the assumption for pairwise keys.
		 */
		if (key &&
		    ((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
		     (old_key && old_key->conf.cipher != key->conf.cipher)))
			goto out;
	} else if (sta) {
		old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
	else
	} else {
		old_key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
	}

	/* Non-pairwise keys must also not switch the cipher on rekey */
	if (!pairwise) {
		if (key && old_key && old_key->conf.cipher != key->conf.cipher)
			goto out;
	}

	/*
	 * Silently accept key re-installation without really installing the
Loading