From 96fc6efb9ad9d0cd8cbb4462f0eb2a07092649e6 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Tue, 19 Mar 2019 21:34:08 +0100 Subject: 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: Alexander Wetzel [remove module parameter] Signed-off-by: Johannes Berg --- net/mac80211/key.c | 63 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 14 deletions(-) (limited to 'net/mac80211/key.c') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 41b8db37c7c1..42d52cded4c1 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -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); - sta->ptk_idx = idx; - if (new) { + if (new && + !(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)) { + sta->ptk_idx = idx; 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 -- cgit v1.2.3