]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Shuffle failed auth requests before sending the failure replies.
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Sun, 9 Apr 2017 12:31:11 +0000 (15:31 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 12 Apr 2017 22:19:42 +0000 (01:19 +0300)
This might be helpful against some timing attacks.

Using Fisher–Yates shuffle.

src/auth/auth-request-handler.c

index f3db0e2117b40ede0ae7359e791e0e9d9a578219..68becc2835ec441d3920cea58ebbc49ebdc87c41 100644 (file)
@@ -805,7 +805,7 @@ void auth_request_handler_cancel_request(struct auth_request_handler *handler,
 void auth_request_handler_flush_failures(bool flush_all)
 {
        struct auth_request **auth_requests, *auth_request;
-       unsigned int i, count;
+       unsigned int i, j, count;
        time_t diff;
 
        count = aqueue_count(auth_failures);
@@ -816,15 +816,34 @@ void auth_request_handler_flush_failures(bool flush_all)
        }
 
        auth_requests = array_idx_modifiable(&auth_failures_arr, 0);
+       /* count the number of requests that we need to flush */
        for (i = 0; i < count; i++) {
-               auth_request = auth_requests[aqueue_idx(auth_failures, 0)];
+               auth_request = auth_requests[aqueue_idx(auth_failures, i)];
 
                /* FIXME: assumess that failure_delay is always the same. */
                diff = ioloop_time - auth_request->last_access;
                if (diff < (time_t)auth_request->set->failure_delay &&
                    !flush_all)
                        break;
+       }
+
+       /* shuffle these requests to try to prevent any kind of timing attacks
+          where attacker performs multiple requests in parallel and attempts
+          to figure out results based on the order of replies. */
+       count = i;
+       for (i = 0; i < count; i++) {
+               j = random() % (count - i) + i;
+               auth_request = auth_requests[aqueue_idx(auth_failures, i)];
 
+               /* swap i & j */
+               auth_requests[aqueue_idx(auth_failures, i)] =
+                       auth_requests[aqueue_idx(auth_failures, j)];
+               auth_requests[aqueue_idx(auth_failures, j)] = auth_request;
+       }
+
+       /* flush the requests */
+       for (i = 0; i < count; i++) {
+               auth_request = auth_requests[aqueue_idx(auth_failures, i)];
                aqueue_delete_tail(auth_failures);
 
                i_assert(auth_request->state == AUTH_REQUEST_STATE_FINISHED);