diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index c41d2e851..1f7d8d65a 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -8,6 +8,10 @@ ChangeLog for wpa_supplicant * added a new network configuration option, wpa_ptk_rekey, that can be used to enforce frequent PTK rekeying, e.g., to mitigate some attacks against TKIP deficiencies + * added an optional mitigation mechanism for certain attacks against + TKIP by delaying Michael MIC error reports by a random amount of time + between 0 and 60 seconds; this can be enabled with a build option + CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config * fixed EAP-AKA to use RES Length field in AT_RES as length in bits, not bytes diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index badda34da..277894c13 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -951,6 +951,10 @@ ifdef CONFIG_DEBUG_FILE CFLAGS += -DCONFIG_DEBUG_FILE endif +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + OBJS += ../src/drivers/scan_helpers.o OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 38435cd10..2653654eb 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -364,3 +364,7 @@ CONFIG_PEERKEY=y # Enable privilege separation (see README 'Privilege separation' for details) #CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 45b5ac3a3..27dfd55ff 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -862,6 +862,22 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->pending_mic_error_report) + return; + + wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report"); + wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise); + wpa_s->pending_mic_error_report = 0; +} +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + + static void wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -871,10 +887,25 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); - wpa_sm_key_request(wpa_s->wpa, 1, pairwise); os_get_time(&t); - if (wpa_s->last_michael_mic_error && - t.sec - wpa_s->last_michael_mic_error <= 60) { + if ((wpa_s->last_michael_mic_error && + t.sec - wpa_s->last_michael_mic_error <= 60) || + wpa_s->pending_mic_error_report) { + if (wpa_s->pending_mic_error_report) { + /* + * Send the pending MIC error report immediately since + * we are going to start countermeasures and AP better + * do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, + wpa_s->pending_mic_error_pairwise); + } + + /* Send the new MIC error report immediately since we are going + * to start countermeasures and AP better do the same. + */ + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + /* initialize countermeasures */ wpa_s->countermeasures = 1; wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); @@ -895,8 +926,46 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, wpa_s, NULL); /* TODO: mark the AP rejected for 60 second. STA is * allowed to associate with another AP.. */ + } else { +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT + if (wpa_s->mic_errors_seen) { + /* + * Reduce the effectiveness of Michael MIC error + * reports as a means for attacking against TKIP if + * more than one MIC failure is noticed with the same + * PTK. We delay the transmission of the reports by a + * random time between 0 and 60 seconds in order to + * force the attacker wait 60 seconds before getting + * the information on whether a frame resulted in a MIC + * failure. + */ + u8 rval[4]; + int sec; + + if (os_get_random(rval, sizeof(rval)) < 0) + sec = os_random() % 60; + else + sec = WPA_GET_BE32(rval) % 60; + wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d " + "seconds", sec); + wpa_s->pending_mic_error_report = 1; + wpa_s->pending_mic_error_pairwise = pairwise; + eloop_cancel_timeout( + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + eloop_register_timeout( + sec, os_random() % 1000000, + wpa_supplicant_delayed_mic_error_report, + wpa_s, NULL); + } else { + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); + } +#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */ + wpa_sm_key_request(wpa_s->wpa, 1, pairwise); +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ } wpa_s->last_michael_mic_error = t.sec; + wpa_s->mic_errors_seen++; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 9afae2a98..a8fb6d22b 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -334,6 +334,10 @@ struct wpa_supplicant { struct wpa_client_mlme mlme; int use_client_mlme; int driver_4way_handshake; + + int pending_mic_error_report; + int pending_mic_error_pairwise; + int mic_errors_seen; /* Michael MIC errors with the current PTK */ }; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index d5e31ebb0..e1e2cd66e 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -427,11 +427,16 @@ static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid) } -static int wpa_supplicant_set_key(void *wpa_s, wpa_alg alg, +static int wpa_supplicant_set_key(void *_wpa_s, wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { + struct wpa_supplicant *wpa_s = _wpa_s; + if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) { + /* Clear the MIC error counter when setting a new PTK. */ + wpa_s->mic_errors_seen = 0; + } return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); }