Added an optional mitigation mechanism for certain attacks against TKIP by
[libeap.git] / wpa_supplicant / events.c
index 45b5ac3..27dfd55 100644 (file)
@@ -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++;
 }