nl80211: Support GTK rekey offload
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>
This commit is contained in:
parent
7aec3776b9
commit
b14a210ce2
8 changed files with 170 additions and 1 deletions
|
@ -2254,6 +2254,20 @@ struct wpa_driver_ops {
|
||||||
* implementation, there is no need to implement this function.
|
* implementation, there is no need to implement this function.
|
||||||
*/
|
*/
|
||||||
int (*set_authmode)(void *priv, int authmode);
|
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 - 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 {
|
struct ibss_peer_lost {
|
||||||
u8 peer[ETH_ALEN];
|
u8 peer[ETH_ALEN];
|
||||||
} ibss_peer_lost;
|
} 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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)
|
static int process_event(struct nl_msg *msg, void *arg)
|
||||||
{
|
{
|
||||||
struct wpa_driver_nl80211_data *drv = 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:
|
case NL80211_CMD_DEL_STATION:
|
||||||
nl80211_del_station_event(drv, tb);
|
nl80211_del_station_event(drv, tb);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_CMD_SET_REKEY_OFFLOAD:
|
||||||
|
nl80211_rekey_offload_event(drv, tb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
|
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
|
||||||
"(cmd=%d)", gnlh->cmd);
|
"(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 = {
|
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
||||||
.name = "nl80211",
|
.name = "nl80211",
|
||||||
.desc = "Linux nl80211/cfg80211",
|
.desc = "Linux nl80211/cfg80211",
|
||||||
|
@ -6717,4 +6797,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
|
||||||
.add_pmkid = nl80211_add_pmkid,
|
.add_pmkid = nl80211_add_pmkid,
|
||||||
.remove_pmkid = nl80211_remove_pmkid,
|
.remove_pmkid = nl80211_remove_pmkid,
|
||||||
.flush_pmkid = nl80211_flush_pmkid,
|
.flush_pmkid = nl80211_flush_pmkid,
|
||||||
|
.set_rekey_info = nl80211_set_rekey_info,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1174,6 +1174,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wpa_sm_set_rekey_offload(sm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
failed:
|
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));
|
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
|
||||||
wpa_sm_cancel_auth_timeout(sm);
|
wpa_sm_cancel_auth_timeout(sm);
|
||||||
wpa_sm_set_state(sm, WPA_COMPLETED);
|
wpa_sm_set_state(sm, WPA_COMPLETED);
|
||||||
|
|
||||||
|
wpa_sm_set_rekey_offload(sm);
|
||||||
} else {
|
} else {
|
||||||
wpa_supplicant_key_neg_complete(sm, sm->bssid,
|
wpa_supplicant_key_neg_complete(sm, sm->bssid,
|
||||||
key_info &
|
key_info &
|
||||||
|
@ -2644,3 +2648,9 @@ int wpa_sm_has_ptk(struct wpa_sm *sm)
|
||||||
return 0;
|
return 0;
|
||||||
return sm->ptk_set;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ struct wpa_sm_ctx {
|
||||||
u16 status_code, const u8 *buf, size_t len);
|
u16 status_code, const u8 *buf, size_t len);
|
||||||
int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
|
int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
|
||||||
#endif /* CONFIG_TDLS */
|
#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);
|
void wpa_sm_drop_sa(struct wpa_sm *sm);
|
||||||
int wpa_sm_has_ptk(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 */
|
#else /* CONFIG_NO_WPA */
|
||||||
|
|
||||||
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
|
||||||
|
const u8 *replay_ctr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_NO_WPA */
|
#endif /* CONFIG_NO_WPA */
|
||||||
|
|
||||||
#ifdef CONFIG_PEERKEY
|
#ifdef CONFIG_PEERKEY
|
||||||
|
|
|
@ -244,6 +244,14 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
|
||||||
return -1;
|
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
|
#ifdef CONFIG_TDLS
|
||||||
static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
|
static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
|
||||||
u8 action_code, u8 dialog_token,
|
u8 action_code, u8 dialog_token,
|
||||||
|
|
|
@ -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);
|
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 */
|
#endif /* DRIVER_I_H */
|
||||||
|
|
|
@ -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);
|
ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
|
||||||
#endif /* CONFIG_IBSS_RSN */
|
#endif /* CONFIG_IBSS_RSN */
|
||||||
break;
|
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:
|
default:
|
||||||
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
|
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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)
|
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_NO_WPA
|
#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->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
|
||||||
ctx->tdls_oper = wpa_supplicant_tdls_oper;
|
ctx->tdls_oper = wpa_supplicant_tdls_oper;
|
||||||
#endif /* CONFIG_TDLS */
|
#endif /* CONFIG_TDLS */
|
||||||
|
ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
|
||||||
|
|
||||||
wpa_s->wpa = wpa_sm_init(ctx);
|
wpa_s->wpa = wpa_sm_init(ctx);
|
||||||
if (wpa_s->wpa == NULL) {
|
if (wpa_s->wpa == NULL) {
|
||||||
|
|
Loading…
Reference in a new issue