nl80211: Support GTK rekey offload
authorJohannes Berg <johannes.berg@intel.com>
Tue, 12 Jul 2011 18:22:51 +0000 (21:22 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 12 Jul 2011 18:22:51 +0000 (21:22 +0300)
Add support to wpa_supplicant for device-based GTK rekeying. In order to
support that, pass the KEK, KCK, and replay counter to the driver, and
handle rekey events that update the latter.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
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/driver_i.h
wpa_supplicant/events.c
wpa_supplicant/wpas_glue.c

index 1f9a4c8..828948d 100644 (file)
@@ -2254,6 +2254,20 @@ struct wpa_driver_ops {
         * implementation, there is no need to implement this function.
         */
        int (*set_authmode)(void *priv, int authmode);
+
+       /**
+        * set_rekey_info - Set rekey information
+        * @priv: Private driver interface data
+        * @kek: Current KEK
+        * @kck: Current KCK
+        * @replay_ctr: Current EAPOL-Key Replay Counter
+        *
+        * This optional function can be used to provide information for the
+        * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+        * while the host (including wpa_supplicant) is sleeping.
+        */
+       void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+                              const u8 *replay_ctr);
 };
 
 
@@ -2656,7 +2670,17 @@ enum wpa_event_type {
        /**
         * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
         */
-       EVENT_IBSS_PEER_LOST
+       EVENT_IBSS_PEER_LOST,
+
+       /**
+        * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+        *
+        * This event carries the new replay counter to notify wpa_supplicant
+        * of the current EAPOL-Key Replay Counter in case the driver/firmware
+        * completed Group Key Handshake while the host (including
+        * wpa_supplicant was sleeping).
+        */
+       EVENT_DRIVER_GTK_REKEY
 };
 
 
@@ -3188,6 +3212,14 @@ union wpa_event_data {
        struct ibss_peer_lost {
                u8 peer[ETH_ALEN];
        } ibss_peer_lost;
+
+       /**
+        * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+        */
+       struct driver_gtk_rekey {
+               const u8 *bssid;
+               const u8 *replay_ctr;
+       } driver_gtk_rekey;
 };
 
 /**
index 1da5092..d680ac2 100644 (file)
@@ -1376,6 +1376,48 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+                                       struct nlattr **tb)
+{
+       struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+       static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+               [NL80211_REKEY_DATA_KEK] = {
+                       .minlen = NL80211_KEK_LEN,
+                       .maxlen = NL80211_KEK_LEN,
+               },
+               [NL80211_REKEY_DATA_KCK] = {
+                       .minlen = NL80211_KCK_LEN,
+                       .maxlen = NL80211_KCK_LEN,
+               },
+               [NL80211_REKEY_DATA_REPLAY_CTR] = {
+                       .minlen = NL80211_REPLAY_CTR_LEN,
+                       .maxlen = NL80211_REPLAY_CTR_LEN,
+               },
+       };
+       union wpa_event_data data;
+
+       if (!tb[NL80211_ATTR_MAC])
+               return;
+       if (!tb[NL80211_ATTR_REKEY_DATA])
+               return;
+       if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
+               return;
+       if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+                  MAC2STR(data.driver_gtk_rekey.bssid));
+       data.driver_gtk_rekey.replay_ctr =
+               nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+       wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+                   data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+       wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
 static int process_event(struct nl_msg *msg, void *arg)
 {
        struct wpa_driver_nl80211_data *drv = arg;
@@ -1494,6 +1536,9 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_DEL_STATION:
                nl80211_del_station_event(drv, tb);
                break;
+       case NL80211_CMD_SET_REKEY_OFFLOAD:
+               nl80211_rekey_offload_event(drv, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -6645,6 +6690,41 @@ static int nl80211_flush_pmkid(void *priv)
 }
 
 
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+                                  const u8 *replay_ctr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nlattr *replay_nested;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_REKEY_OFFLOAD, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+       if (!replay_nested)
+               goto nla_put_failure;
+
+       NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek);
+       NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck);
+       NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+               replay_ctr);
+
+       nla_nest_end(msg, replay_nested);
+
+       send_and_recv_msgs(drv, msg, NULL, NULL);
+       return;
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -6717,4 +6797,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .add_pmkid = nl80211_add_pmkid,
        .remove_pmkid = nl80211_remove_pmkid,
        .flush_pmkid = nl80211_flush_pmkid,
+       .set_rekey_info = nl80211_set_rekey_info,
 };
index 7c0ac87..047dcc1 100644 (file)
@@ -1174,6 +1174,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
        }
 
+       wpa_sm_set_rekey_offload(sm);
+
        return;
 
 failed:
@@ -1392,6 +1394,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
                        MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
                wpa_sm_cancel_auth_timeout(sm);
                wpa_sm_set_state(sm, WPA_COMPLETED);
+
+               wpa_sm_set_rekey_offload(sm);
        } else {
                wpa_supplicant_key_neg_complete(sm, sm->bssid,
                                                key_info &
@@ -2644,3 +2648,9 @@ int wpa_sm_has_ptk(struct wpa_sm *sm)
                return 0;
        return sm->ptk_set;
 }
+
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
+{
+       os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
+}
index 111597c..48090e0 100644 (file)
@@ -61,6 +61,8 @@ struct wpa_sm_ctx {
                              u16 status_code, const u8 *buf, size_t len);
        int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
 #endif /* CONFIG_TDLS */
+       void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+                                 const u8 *replay_ctr);
 };
 
 
@@ -132,6 +134,8 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
 
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -277,6 +281,11 @@ static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
        return 0;
 }
 
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+                                           const u8 *replay_ctr)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
index 09a2e4f..ebe73ca 100644 (file)
@@ -244,6 +244,14 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
        return -1;
 }
 
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+       if (!sm->ctx->set_rekey_offload)
+               return;
+       sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
+                                  sm->ptk.kck, sm->rx_replay_counter);
+}
+
 #ifdef CONFIG_TDLS
 static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
                                        u8 action_code, u8 dialog_token,
index 0d436dd..8637754 100644 (file)
@@ -704,4 +704,13 @@ static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
        return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
 }
 
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+                                         const u8 *kek, const u8 *kck,
+                                         const u8 *replay_ctr)
+{
+       if (!wpa_s->driver->set_rekey_info)
+               return;
+       wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
+}
+
 #endif /* DRIVER_I_H */
index 9905cc0..b398792 100644 (file)
@@ -2214,6 +2214,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
 #endif /* CONFIG_IBSS_RSN */
                break;
+       case EVENT_DRIVER_GTK_REKEY:
+               if (os_memcmp(data->driver_gtk_rekey.bssid,
+                             wpa_s->bssid, ETH_ALEN))
+                       break;
+               if (!wpa_s->wpa)
+                       break;
+               wpa_sm_update_replay_ctr(wpa_s->wpa,
+                                        data->driver_gtk_rekey.replay_ctr);
+               break;
        default:
                wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;
index edb9475..98b082c 100644 (file)
@@ -667,6 +667,16 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
 }
 
 
+static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
+                                            const u8 *kck,
+                                            const u8 *replay_ctr)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -706,6 +716,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
        ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
        ctx->tdls_oper = wpa_supplicant_tdls_oper;
 #endif /* CONFIG_TDLS */
+       ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
 
        wpa_s->wpa = wpa_sm_init(ctx);
        if (wpa_s->wpa == NULL) {