changeset 21954:1c952a42bf12

auth: Shuffle failed auth requests before sending the failure replies. This might be helpful against some timing attacks. Using Fisher–Yates shuffle.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Sun, 09 Apr 2017 15:31:11 +0300
parents ec6539bd0690
children 64d17b868bcc
files src/auth/auth-request-handler.c
diffstat 1 files changed, 21 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-request-handler.c	Sun Apr 09 15:19:25 2017 +0300
+++ b/src/auth/auth-request-handler.c	Sun Apr 09 15:31:11 2017 +0300
@@ -805,7 +805,7 @@
 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 @@
 	}
 
 	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);