]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/mac80211/key.c
Merge tag 'mac80211-next-for-davem-2019-04-26' of git://git.kernel.org/pub/scm/linux...
[linux.git] / net / mac80211 / key.c
index 4700718e010f5a886001e9a0a0326a628edf0739..20bf9db7a3886de7601fabd0e0ff2c721e54e30e 100644 (file)
@@ -140,6 +140,12 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                 * so clear that flag now to avoid trying to remove
                 * it again later.
                 */
+               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+                   !(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
+                                        IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
+                                        IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
+                       increment_tailroom_need_count(sdata);
+
                key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
                return -EINVAL;
        }
@@ -167,8 +173,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                 * The driver doesn't know anything about VLAN interfaces.
                 * Hence, don't send GTKs for VLAN interfaces to the driver.
                 */
-               if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+                       ret = 1;
                        goto out_unsupported;
+               }
        }
 
        ret = drv_set_key(key->local, SET_KEY, sdata,
@@ -177,9 +185,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
        if (!ret) {
                key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
 
-               if (!((key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
-                                          IEEE80211_KEY_FLAG_PUT_MIC_SPACE)) ||
-                     (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
+               if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
+                                        IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
+                                        IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
                        decrease_tailroom_need_count(sdata, 1);
 
                WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
@@ -213,11 +221,8 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                /* all of these we can do in software - if driver can */
                if (ret == 1)
                        return 0;
-               if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL)) {
-                       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-                               return 0;
+               if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL))
                        return -EINVAL;
-               }
                return 0;
        default:
                return -EINVAL;
@@ -243,9 +248,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
        sta = key->sta;
        sdata = key->sdata;
 
-       if (!((key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
-                                  IEEE80211_KEY_FLAG_PUT_MIC_SPACE)) ||
-             (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
+       if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
+                                IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
+                                IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
                increment_tailroom_need_count(sdata);
 
        key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
@@ -259,9 +264,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;
@@ -278,8 +298,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;
 
@@ -395,10 +416,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 */
@@ -415,15 +432,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 &&
@@ -739,16 +761,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
@@ -1188,9 +1228,9 @@ void ieee80211_remove_key(struct ieee80211_key_conf *keyconf)
        if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
                key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
 
-               if (!((key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
-                                          IEEE80211_KEY_FLAG_PUT_MIC_SPACE)) ||
-                     (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
+               if (!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
+                                        IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
+                                        IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
                        increment_tailroom_need_count(key->sdata);
        }