From e18b4e41d9718a199a1980688787c2743c870002 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Sun, 9 Apr 2017 15:31:11 +0300 Subject: [PATCH] auth: Shuffle failed auth requests before sending the failure replies. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This might be helpful against some timing attacks. Using Fisher–Yates shuffle. --- src/auth/auth-request-handler.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/auth/auth-request-handler.c b/src/auth/auth-request-handler.c index 928f713dad..2c7468969d 100644 --- a/src/auth/auth-request-handler.c +++ b/src/auth/auth-request-handler.c @@ -827,7 +827,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); @@ -838,15 +838,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);