Prevent reinstallation of an already in-use group key

Track the current GTK and IGTK that is in use and when receiving a
(possibly retransmitted) Group Message 1 or WNM-Sleep Mode Response, do
not install the given key if it is already in use. This prevents an
attacker from trying to trick the client into resetting or lowering the
sequence counter associated to the group key.

Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
This commit is contained in:
Mathy Vanhoef 2017-07-12 16:03:24 +02:00 committed by Jouni Malinen
parent 0e3bd7ac68
commit cb5132bb35
3 changed files with 87 additions and 44 deletions

View File

@ -221,6 +221,17 @@ struct wpa_ptk {
size_t tk_len; size_t tk_len;
}; };
struct wpa_gtk {
u8 gtk[WPA_GTK_MAX_LEN];
size_t gtk_len;
};
#ifdef CONFIG_IEEE80211W
struct wpa_igtk {
u8 igtk[WPA_IGTK_MAX_LEN];
size_t igtk_len;
};
#endif /* CONFIG_IEEE80211W */
/* WPA IE version 1 /* WPA IE version 1
* 00-50-f2:1 (OUI:OUI type) * 00-50-f2:1 (OUI:OUI type)

View File

@ -802,6 +802,15 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
const u8 *_gtk = gd->gtk; const u8 *_gtk = gd->gtk;
u8 gtk_buf[32]; u8 gtk_buf[32];
/* Detect possible key reinstallation */
if (sm->gtk.gtk_len == (size_t) gd->gtk_len &&
os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
gd->keyidx, gd->tx, gd->gtk_len);
return 0;
}
wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
@ -836,6 +845,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
} }
os_memset(gtk_buf, 0, sizeof(gtk_buf)); os_memset(gtk_buf, 0, sizeof(gtk_buf));
sm->gtk.gtk_len = gd->gtk_len;
os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
return 0; return 0;
} }
@ -942,6 +954,48 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
} }
#ifdef CONFIG_IEEE80211W
static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
const struct wpa_igtk_kde *igtk)
{
size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
u16 keyidx = WPA_GET_LE16(igtk->keyid);
/* Detect possible key reinstallation */
if (sm->igtk.igtk_len == len &&
os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
keyidx);
return 0;
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x",
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
if (keyidx > 4095) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to configure IGTK to the driver");
return -1;
}
sm->igtk.igtk_len = len;
os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
return 0;
}
#endif /* CONFIG_IEEE80211W */
static int ieee80211w_set_keys(struct wpa_sm *sm, static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie) struct wpa_eapol_ie_parse *ie)
{ {
@ -952,30 +1006,14 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
if (ie->igtk) { if (ie->igtk) {
size_t len; size_t len;
const struct wpa_igtk_kde *igtk; const struct wpa_igtk_kde *igtk;
u16 keyidx;
len = wpa_cipher_key_len(sm->mgmt_group_cipher); len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
return -1; return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk; igtk = (const struct wpa_igtk_kde *) ie->igtk;
keyidx = WPA_GET_LE16(igtk->keyid); if (wpa_supplicant_install_igtk(sm, igtk) < 0)
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
"pn %02x%02x%02x%02x%02x%02x",
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
igtk->igtk, len);
if (keyidx > 4095) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid IGTK KeyID %d", keyidx);
return -1; return -1;
}
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to configure IGTK to the driver");
return -1;
}
} }
return 0; return 0;
@ -2493,7 +2531,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
*/ */
void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
{ {
int clear_ptk = 1; int clear_keys = 1;
if (sm == NULL) if (sm == NULL)
return; return;
@ -2519,7 +2557,7 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
/* Prepare for the next transition */ /* Prepare for the next transition */
wpa_ft_prepare_auth_request(sm, NULL); wpa_ft_prepare_auth_request(sm, NULL);
clear_ptk = 0; clear_keys = 0;
} }
#endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_FILS #ifdef CONFIG_FILS
@ -2529,11 +2567,11 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
* AUTHENTICATED state to get the EAPOL port Authorized. * AUTHENTICATED state to get the EAPOL port Authorized.
*/ */
wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
clear_ptk = 0; clear_keys = 0;
} }
#endif /* CONFIG_FILS */ #endif /* CONFIG_FILS */
if (clear_ptk) { if (clear_keys) {
/* /*
* IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
* this is not part of a Fast BSS Transition. * this is not part of a Fast BSS Transition.
@ -2543,6 +2581,10 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk));
sm->tptk_set = 0; sm->tptk_set = 0;
os_memset(&sm->tptk, 0, sizeof(sm->tptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
#ifdef CONFIG_IEEE80211W
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
#endif /* CONFIG_IEEE80211W */
} }
#ifdef CONFIG_TDLS #ifdef CONFIG_TDLS
@ -3119,6 +3161,10 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(sm->pmk, 0, sizeof(sm->pmk));
os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
#ifdef CONFIG_IEEE80211W
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R #ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
@ -3191,29 +3237,11 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
os_memset(&gd, 0, sizeof(gd)); os_memset(&gd, 0, sizeof(gd));
#ifdef CONFIG_IEEE80211W #ifdef CONFIG_IEEE80211W
} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
struct wpa_igtk_kde igd; const struct wpa_igtk_kde *igtk;
u16 keyidx;
os_memset(&igd, 0, sizeof(igd)); igtk = (const struct wpa_igtk_kde *) (buf + 2);
keylen = wpa_cipher_key_len(sm->mgmt_group_cipher); if (wpa_supplicant_install_igtk(sm, igtk) < 0)
os_memcpy(igd.keyid, buf + 2, 2);
os_memcpy(igd.pn, buf + 4, 6);
keyidx = WPA_GET_LE16(igd.keyid);
os_memcpy(igd.igtk, buf + 10, keylen);
wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
igd.igtk, keylen);
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igd.pn, sizeof(igd.pn),
igd.igtk, keylen) < 0) {
wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
"WNM mode");
os_memset(&igd, 0, sizeof(igd));
return -1; return -1;
}
os_memset(&igd, 0, sizeof(igd));
#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_IEEE80211W */
} else { } else {
wpa_printf(MSG_DEBUG, "Unknown element id"); wpa_printf(MSG_DEBUG, "Unknown element id");

View File

@ -31,6 +31,10 @@ struct wpa_sm {
u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
int rx_replay_counter_set; int rx_replay_counter_set;
u8 request_counter[WPA_REPLAY_COUNTER_LEN]; u8 request_counter[WPA_REPLAY_COUNTER_LEN];
struct wpa_gtk gtk;
#ifdef CONFIG_IEEE80211W
struct wpa_igtk igtk;
#endif /* CONFIG_IEEE80211W */
struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */