diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 1f9a4c867..828948d88 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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; }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1da509225..d680ac23d 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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, }; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 7c0ac8739..047dcc11c 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -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); +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 111597ca4..48090e0c8 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -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 diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 09a2e4ff5..ebe73ca9c 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -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, diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 0d436dd1f..8637754fb 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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 */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 9905cc008..b39879249 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -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; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index edb947529..98b082c89 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -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) {