]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Add support for offloading key management operations to the driver
authorChet Lanctot <clanctot@qca.qualcomm.com>
Thu, 23 Oct 2014 15:21:49 +0000 (18:21 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 23 Oct 2014 15:38:50 +0000 (18:38 +0300)
This commit introduces a QCA vendor command and event to provide an
option to use extended versions of the nl80211 connect/roam operations
in a way that allows drivers to offload key management operations to the
driver/firmware.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
12 files changed:
src/common/qca-vendor.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/events.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpas_glue.c

index ad3bdfd68fee17e8d13ec6bffdca028b5a1ef1aa..7e3057e14ed34c2eda31f6e551abafe96c0c167f 100644 (file)
@@ -50,6 +50,14 @@ enum qca_radiotap_vendor_ids {
  * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
  *     NAN Request/Response and NAN Indication messages. These messages are
  *     interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ *     used to configure PMK to the driver even when not connected. This can
+ *     be used to request offloading of key management operations.
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ *     NL80211_CMD_ROAM event with optional attributes including information
+ *     from offloaded key management operation. Uses
+ *     enum qca_wlan_vendor_attr_roam_auth attributes.
  */
 enum qca_nl80211_vendor_subcmds {
        QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -61,6 +69,8 @@ enum qca_nl80211_vendor_subcmds {
        QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
        QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
        /* 14..49 - reserved for QCA */
+       QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+       QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
 };
 
 
@@ -89,4 +99,19 @@ enum qca_roaming_policy {
        QCA_ROAMING_ALLOWED_WITHIN_ESS,
 };
 
+enum qca_wlan_vendor_attr_roam_auth {
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+       /* keep last */
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
 #endif /* QCA_VENDOR_H */
index ad9d08014f6e2995dee89bb3d1edfa6d42c7688c..7f1ee82e816c9a095c3f01de8e3b3a56009cfd92 100644 (file)
@@ -699,6 +699,14 @@ struct wpa_driver_associate_params {
        const struct ieee80211_vht_capabilities *vhtcaps;
        const struct ieee80211_vht_capabilities *vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
+
+       /**
+        * req_key_mgmt_offload - Request key management offload for connection
+        *
+        * Request key management offload for this connection if the device
+        * supports it.
+        */
+       int req_key_mgmt_offload;
 };
 
 enum hide_ssid {
@@ -3677,6 +3685,54 @@ union wpa_event_data {
                 * addr - Station address (for AP mode)
                 */
                const u8 *addr;
+
+               /**
+                * The following is the key management offload information
+                * @authorized
+                * @key_replay_ctr
+                * @key_replay_ctr_len
+                * @ptk_kck
+                * @ptk_kek_len
+                * @ptk_kek
+                * @ptk_kek_len
+                */
+
+               /**
+                * authorized - Status of key management offload,
+                * 1 = successful
+                */
+               int authorized;
+
+               /**
+                * key_replay_ctr - Key replay counter value last used
+                * in a valid EAPOL-Key frame
+                */
+               const u8 *key_replay_ctr;
+
+               /**
+                * key_replay_ctr_len - The length of key_replay_ctr
+                */
+               size_t key_replay_ctr_len;
+
+               /**
+                * ptk_kck - The derived PTK KCK
+                */
+               const u8 *ptk_kck;
+
+               /**
+                * ptk_kek_len - The length of ptk_kck
+                */
+               size_t ptk_kck_len;
+
+               /**
+                * ptk_kek - The derived PTK KEK
+                */
+               const u8 *ptk_kek;
+
+               /**
+                * ptk_kek_len - The length of ptk_kek
+                */
+               size_t ptk_kek_len;
        } assoc_info;
 
        /**
index 158f1715c69cc05b31001d7f6a10f5e6b4df3c89..b8df564c32ce712fcc2bc959fa6b202a262cf829 100644 (file)
@@ -315,6 +315,8 @@ struct wpa_driver_nl80211_data {
        unsigned int have_low_prio_scan:1;
        unsigned int force_connect_cmd:1;
        unsigned int addr_changed:1;
+       unsigned int key_mgmt_set_key_vendor_cmd_avail:1;
+       unsigned int roam_auth_vendor_event_avail:1;
 
        u64 remain_on_chan_cookie;
        u64 send_action_cookie;
@@ -1587,7 +1589,11 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
 static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                               enum nl80211_commands cmd, struct nlattr *status,
                               struct nlattr *addr, struct nlattr *req_ie,
-                              struct nlattr *resp_ie)
+                              struct nlattr *resp_ie,
+                              struct nlattr *authorized,
+                              struct nlattr *key_replay_ctr,
+                              struct nlattr *ptk_kck,
+                              struct nlattr *ptk_kek)
 {
        union wpa_event_data event;
 
@@ -1637,6 +1643,23 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 
        event.assoc_info.freq = nl80211_get_assoc_freq(drv);
 
+       if (authorized && nla_get_u8(authorized)) {
+               event.assoc_info.authorized = 1;
+               wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+       }
+       if (key_replay_ctr) {
+               event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+               event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+       }
+       if (ptk_kck) {
+               event.assoc_info.ptk_kck = nla_data(ptk_kck);
+               event.assoc_info.ptk_kck_len = nla_len(ptk_kek);
+       }
+       if (ptk_kek) {
+               event.assoc_info.ptk_kek = nla_data(ptk_kek);
+               event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+       }
+
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -2976,6 +2999,39 @@ static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+                                     const u8 *data, size_t len)
+{
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+       u8 *bssid;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Key management roam+auth vendor event received");
+
+       if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+                     (struct nlattr *) data, len, NULL))
+               return;
+       if (!tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+           nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+               return;
+
+       bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+       wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
+
+       mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+}
+
+
 static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
                                     u32 subcmd, u8 *data, size_t len)
 {
@@ -2983,6 +3039,9 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
        case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
                qca_nl80211_avoid_freq(drv, data, len);
                break;
+       case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+               qca_nl80211_key_mgmt_auth(drv, data, len);
+               break;
        default:
                wpa_printf(MSG_DEBUG,
                           "nl80211: Ignore unsupported QCA vendor event %u",
@@ -3109,6 +3168,17 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
                   cmd, nl80211_command_to_string(cmd), bss->ifname);
 
+       if (cmd == NL80211_CMD_ROAM && drv->roam_auth_vendor_event_avail) {
+               /*
+                * Device will use roam+auth vendor event to indicate
+                * roaming, so ignore the regular roam event.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+                          cmd);
+               return;
+       }
+
        if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
            (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
             cmd == NL80211_CMD_SCAN_ABORTED)) {
@@ -3187,7 +3257,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                                   tb[NL80211_ATTR_STATUS_CODE],
                                   tb[NL80211_ATTR_MAC],
                                   tb[NL80211_ATTR_REQ_IE],
-                                  tb[NL80211_ATTR_RESP_IE]);
+                                  tb[NL80211_ATTR_RESP_IE],
+                                  NULL, NULL, NULL, NULL);
                break;
        case NL80211_CMD_CH_SWITCH_NOTIFY:
                mlme_event_ch_switch(drv,
@@ -3970,6 +4041,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                        case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
                                drv->dfs_vendor_cmd_avail = 1;
                                break;
+                       case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+                               drv->key_mgmt_set_key_vendor_cmd_avail = 1;
+                               break;
                        }
 
                        wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
@@ -3988,6 +4062,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                continue;
                        }
                        vinfo = nla_data(nl);
+                       if (vinfo->subcmd ==
+                           QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH)
+                               drv->roam_auth_vendor_event_avail = 1;
                        wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
                                   vinfo->vendor_id, vinfo->subcmd);
                }
@@ -5883,6 +5960,39 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
 }
 
 
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+                                 const u8 *key, size_t key_len)
+{
+       struct nl_msg *msg;
+       int ret = 0;
+
+       if (!drv->key_mgmt_set_key_vendor_cmd_avail)
+               return 0;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
+       NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                   QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY);
+       NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, key_len, key);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Key management set key failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+       }
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
                                      enum wpa_alg alg, const u8 *addr,
                                      int key_idx, int set_tx,
@@ -5911,6 +6021,13 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
        }
 #endif /* CONFIG_TDLS */
 
+       if (alg == WPA_ALG_PMK && drv->key_mgmt_set_key_vendor_cmd_avail) {
+               wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+                          __func__);
+               ret = issue_key_mgmt_set_key(drv, key, key_len);
+               return ret;
+       }
+
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
@@ -9066,6 +9183,16 @@ static int wpa_driver_nl80211_try_connect(
        int ret;
        int algs;
 
+       if (params->req_key_mgmt_offload && params->psk &&
+           (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+            params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+            params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+               ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+               if (ret)
+                       return ret;
+       }
+
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
index 4df9e253cab68d737248d9563dd2cc00b64b0ec1..d95c1bd54e2684814746addf7374edb09f6a9adb 100644 (file)
@@ -138,6 +138,24 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 }
 
 
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+       if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+               if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: Cannot set low order 256 bits of MSK for key management offload");
+       } else {
+#endif /* CONFIG_IEEE80211R */
+               if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+       }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
 static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                                  const unsigned char *src_addr,
                                  const u8 *pmkid)
@@ -198,6 +216,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                        wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
                                        "machines", sm->pmk, pmk_len);
                        sm->pmk_len = pmk_len;
+                       wpa_supplicant_key_mgmt_set_pmk(sm);
                        if (sm->proto == WPA_PROTO_RSN &&
                            !wpa_key_mgmt_ft(sm->key_mgmt)) {
                                sa = pmksa_cache_add(sm->pmksa,
@@ -2787,3 +2806,30 @@ int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
 }
 
 #endif /* CONFIG_P2P */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+       if (rx_replay_counter == NULL)
+               return;
+
+       os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       sm->rx_replay_counter_set = 1;
+       wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+                           const u8 *ptk_kek)
+{
+       if (ptk_kck) {
+               os_memcpy(sm->ptk.kck, ptk_kck, 16);
+               wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+       }
+       if (ptk_kek) {
+               os_memcpy(sm->ptk.kek, ptk_kek, 16);
+               wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+       }
+       sm->ptk_set = 1;
+}
index 595fdf2095576720a92c5cd894fa23ea7600bae8..355ed13a16818d82af25f57c3449af1d07f02df9 100644 (file)
@@ -70,6 +70,7 @@ struct wpa_sm_ctx {
 #endif /* CONFIG_TDLS */
        void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
                                  const u8 *replay_ctr);
+       int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
 };
 
 
@@ -148,6 +149,10 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
 
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+                           const u8 *ptk_kek);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -302,6 +307,16 @@ static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
 {
 }
 
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+                                           const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+                                         const u8 *ptk_kek)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
index 839b545b96b8322ba8500a66adf69ae76cad428a..dd5ddfbd1a857a6bf1d8dc46118b65217575446e 100644 (file)
@@ -312,6 +312,16 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
 }
 #endif /* CONFIG_TDLS */
 
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+                                         const u8 *pmk, size_t pmk_len)
+{
+       if (!sm->proactive_key_caching)
+               return 0;
+       if (!sm->ctx->key_mgmt_set_pmk)
+               return -1;
+       return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
 void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
                        int ver, const u8 *dest, u16 proto,
                        u8 *msg, size_t msg_len, u8 *key_mic);
index a610b24164aa6824152b74b2e6d9b3faf9402f17..45a346a9634d1d72eac952eeab2bac0db7926e01 100644 (file)
@@ -3327,6 +3327,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->wmm_ac_params[3] = ac_vo;
        config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
        config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+       config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
 
        if (ctrl_interface)
                config->ctrl_interface = os_strdup(ctrl_interface);
@@ -3953,6 +3954,7 @@ static const struct global_parse_data global_fields[] = {
        { INT(mac_addr), 0 },
        { INT(rand_addr_lifetime), 0 },
        { INT(preassoc_mac_addr), 0 },
+       { INT(key_mgmt_offload), 0},
 };
 
 #undef FUNC
index f4c2f8883e54d74ceb8a2357eded0d62a9fc2206..ba6e2d926c0613ec11825444d394a08f952b723b 100644 (file)
@@ -28,6 +28,7 @@
 #define DEFAULT_SCAN_CUR_FREQ 0
 #define DEFAULT_P2P_SEARCH_DELAY 500
 #define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1088,6 +1089,17 @@ struct wpa_config {
         * 2 = like 1, but maintain OUI (with local admin bit set)
         */
        int preassoc_mac_addr;
+
+       /**
+        * key_mgmt_offload - Use key management offload
+        *
+        * Key management offload should be used if the device supports it.
+        * Key management offload is the capability of a device operating as
+        * a station to do the exchange necessary to establish temporal keys
+        * during initial RSN connection, after roaming, or during a PTK
+        * rekeying operation.
+        */
+       int key_mgmt_offload;
 };
 
 
index 17dbab6f3fb4580e00ff086689d2bd0305be3223..1661f88c08f0ed802a9dece32dd9722c9032cec3 100644 (file)
@@ -1192,6 +1192,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 
        if (config->preassoc_mac_addr)
                fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+       if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+               fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
index f9e82ddfba638e377592e83579d258a12f59eb61..54c6ea16b382860f07deab9ede8ece93545a7f0b 100644 (file)
@@ -2894,6 +2894,24 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+                                           union wpa_event_data *data)
+{
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Connection authorized by device, previous state %d",
+               wpa_s->wpa_state);
+       if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+               eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+               eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+       }
+       wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+       wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+                              data->assoc_info.ptk_kek);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
@@ -2934,6 +2952,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
        case EVENT_ASSOC:
                wpa_supplicant_event_assoc(wpa_s, data);
+               if (data && data->assoc_info.authorized)
+                       wpa_supplicant_event_assoc_auth(wpa_s, data);
                break;
        case EVENT_DISASSOC:
                wpas_event_disassoc(wpa_s,
index 27afa8320f4779a7aed81878b9013e94886f0013..044a964321b39c0b2aa8840170c4111c72a907fe 100644 (file)
@@ -1921,6 +1921,22 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
                        params.psk = ssid->psk;
        }
 
+       if (wpa_s->conf->key_mgmt_offload) {
+               if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256)
+                       params.req_key_mgmt_offload =
+                               ssid->proactive_key_caching < 0 ?
+                               wpa_s->conf->okc : ssid->proactive_key_caching;
+               else
+                       params.req_key_mgmt_offload = 1;
+
+               if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+                   ssid->psk_set)
+                       params.psk = ssid->psk;
+       }
+
        params.drop_unencrypted = use_crypt;
 
 #ifdef CONFIG_IEEE80211W
index ea95a4ce03c6db1a283d0b509d6e4418d6234c08..48b854bd39dd25e3c359cff53aa4ef9869d314a6 100644 (file)
@@ -915,6 +915,19 @@ static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
 #endif /* CONFIG_NO_WPA */
 
 
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+                                          size_t pmk_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->conf->key_mgmt_offload)
+               return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+                                      NULL, 0, pmk, pmk_len);
+       else
+               return 0;
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -956,6 +969,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
        ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
 #endif /* CONFIG_TDLS */
        ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+       ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
 
        wpa_s->wpa = wpa_sm_init(ctx);
        if (wpa_s->wpa == NULL) {