cfg80211/mac80211: allow per-station GTKs
authorJohannes Berg <johannes.berg@intel.com>
Tue, 5 Oct 2010 17:39:30 +0000 (19:39 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 6 Oct 2010 20:30:40 +0000 (16:30 -0400)
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>
20 files changed:
drivers/net/wireless/iwmc3200wifi/cfg80211.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/rndis_wlan.c
include/linux/nl80211.h
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tx.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/nl80211.c
net/wireless/sme.c
net/wireless/util.c
net/wireless/wext-compat.c

index 6061967..c6c0eff 100644 (file)
@@ -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];
index cb3b855..1dc44bc 100644 (file)
@@ -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);
index 719573b..71b5971 100644 (file)
@@ -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,
-                                       struct key_params *params);
+                        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,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,
-                                       u8 key_index, const u8 *mac_addr,
-                                       struct key_params *params)
+                        u8 key_index, bool pairwise, const u8 *mac_addr,
+                        struct key_params *params)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        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,
-                                       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;
index c4efdfa..e451f17 100644 (file)
@@ -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,
index 5f4d8ac..0f77515 100644 (file)
@@ -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 {
index 47316a6..33aa2e3 100644 (file)
@@ -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 */
 
 /**
index 94bf550..8b0e874 100644 (file)
@@ -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,10 +173,17 @@ 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);
-                       ret = 0;
+               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];
 
index 76c2b50..f0610fa 100644 (file)
@@ -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;
index 6a63d1a..ccd676b 100644 (file)
@@ -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,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);
 
-       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:
-               case WLAN_CIPHER_SUITE_TKIP:
-               case WLAN_CIPHER_SUITE_CCMP:
-               case WLAN_CIPHER_SUITE_AES_CMAC:
-                       /* all of these we can do in software */
-                       ret = 0;
-                       break;
-               default:
-                       ret = -EINVAL;
-               }
+ out_unsupported:
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               /* all of these we can do in software */
+               return 0;
+       default:
+               return -EINVAL;
        }
-
-       return ret;
 }
 
 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;
 }
 
+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,7 +449,8 @@ static void __ieee80211_key_free(struct ieee80211_key *key)
         */
        if (key->sdata)
                __ieee80211_key_replace(key->sdata, key->sta,
-                                       key, NULL);
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
        __ieee80211_key_destroy(key);
 }
 
index cb9a4a6..0db1c0f 100644 (file)
@@ -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
index b3e161f..b67221d 100644 (file)
@@ -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,7 +912,10 @@ 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 */
-               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)) {
                /*
                 * 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);
                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]);
 
-               /*
-                * 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 &&
-                   rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
-                   !is_multicast_ether_addr(hdr->addr1))
-                       rx->key = NULL;
+               /* 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.
+                        */
+                       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) {
index aeaf2d6..6d8f897 100644 (file)
@@ -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;
 
index cf21a2e..9265aca 100644 (file)
@@ -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;
index 258fbdb..96c5943 100644 (file)
@@ -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) &&
index 37580e0..2d1d4c7 100644 (file)
@@ -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);
index 8cb6e08..f33fbb7 100644 (file)
@@ -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);
index 0c94971..8826888 100644 (file)
@@ -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 ||
index f161b98..e17b0be 100644 (file)
@@ -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));
index fb5448f..76120ae 100644 (file)
@@ -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);
index 7e5c3a4..6002265 100644 (file)
@@ -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, &params);
 }
@@ -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, &params);
 }