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:
Johannes Berg 2011-07-12 21:22:51 +03:00 committed by Jouni Malinen
parent 7aec3776b9
commit b14a210ce2
8 changed files with 170 additions and 1 deletions

View file

@ -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;
}; };
/** /**

View 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) 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,
}; };

View file

@ -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);
}

View file

@ -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

View file

@ -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,

View 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); 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 */

View 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); 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;

View 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) 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) {