diff svr-auth.c @ 1622:e11ed628708b

- Add adaptive authentication failure delay - Rework monotonic_now/gettime_wrapper and use clock_gettime on more platforms
author Matt Johnston <matt@ucc.asn.au>
date Mon, 05 Nov 2018 23:36:34 +0800
parents 1fbe598a14fb
children 592a18dac250
line wrap: on
line diff
--- a/svr-auth.c	Fri Sep 07 23:02:53 2018 +0800
+++ b/svr-auth.c	Mon Nov 05 23:36:34 2018 +0800
@@ -79,6 +79,9 @@
 
 	TRACE(("enter recv_msg_userauth_request"))
 
+	/* for compensating failure delay */
+	gettime_wrapper(&ses.authstate.auth_starttime);
+
 	/* ignore packets if auth is already done */
 	if (ses.authstate.authdone == 1) {
 		TRACE(("leave recv_msg_userauth_request: authdone already"))
@@ -382,16 +385,48 @@
 	encrypt_packet();
 
 	if (incrfail) {
-		unsigned int delay;
-		genrandom((unsigned char*)&delay, sizeof(delay));
-		/* We delay for 300ms +- 50ms */
-		delay = 250000 + (delay % 100000);
+		/* The SSH_MSG_AUTH_FAILURE response is delayed to attempt to
+		avoid user enumeration and slow brute force attempts.
+		The delay is adjusted by the time already spent in processing
+		authentication (ses.authstate.auth_starttime timestamp). */
+
+		/* Desired total delay 300ms +-50ms (in nanoseconds).
+		Beware of integer overflow if increasing these values */
+		const unsigned int mindelay = 250000000;
+		const unsigned int vardelay = 100000000;
+		unsigned int rand_delay;
+		struct timespec delay;
+
+		gettime_wrapper(&delay);
+		delay.tv_sec -= ses.authstate.auth_starttime.tv_sec;
+		delay.tv_nsec -= ses.authstate.auth_starttime.tv_nsec;
+
+		/* carry */
+		if (delay.tv_nsec < 0) {
+			delay.tv_nsec += 1000000000;
+			delay.tv_sec -= 1;
+		}
+
+		genrandom((unsigned char*)&rand_delay, sizeof(rand_delay));
+		rand_delay = mindelay + (rand_delay % vardelay);
+
+		if (delay.tv_sec == 0 && delay.tv_nsec <= mindelay) {
+			/* Compensate for elapsed time */
+			delay.tv_nsec = rand_delay - delay.tv_nsec;
+		} else {
+			/* No time left or time went backwards, just delay anyway */
+			delay.tv_sec = 0;
+			delay.tv_nsec = rand_delay;
+		}
+
+
 #if DROPBEAR_FUZZ
 		if (!fuzz.fuzzing)
 #endif
 		{
-		usleep(delay);
+			while (nanosleep(&delay, &delay) == -1 && errno == EINTR) { /* Go back to sleep */ }
 		}
+
 		ses.authstate.failcount++;
 	}