summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2018-11-05 23:36:34 +0800
committerMatt Johnston <matt@ucc.asn.au>2018-11-05 23:36:34 +0800
commitd7601d7812114f6a7e7f511c3b6840068176782c (patch)
treed4f82f34874a23f2279e08e1c9f248196a1eb9fb
parent8f479150eab6cda9ae421b1caa467c3b3ddf6076 (diff)
downloaddropbear-d7601d7812114f6a7e7f511c3b6840068176782c.tar.gz
- Add adaptive authentication failure delay
- Rework monotonic_now/gettime_wrapper and use clock_gettime on more platforms
-rw-r--r--auth.h3
-rw-r--r--configure.ac6
-rw-r--r--dbutil.c82
-rw-r--r--dbutil.h2
-rw-r--r--includes.h5
-rw-r--r--svr-auth.c45
6 files changed, 95 insertions, 48 deletions
diff --git a/auth.h b/auth.h
index 98f5468..7126a4a 100644
--- a/auth.h
+++ b/auth.h
@@ -108,11 +108,14 @@ struct AuthState {
unsigned int authdone; /* 0 if we haven't authed, 1 if we have. Applies for
client and server (though has differing
meanings). */
+
unsigned int perm_warn; /* Server only, set if bad permissions on
~/.ssh/authorized_keys have already been
logged. */
unsigned int checkusername_failed; /* Server only, set if checkusername
has already failed */
+ struct timespec auth_starttime; /* Server only, time of receiving current
+ SSH_MSG_USERAUTH_REQUEST */
/* These are only used for the server */
uid_t pw_uid;
diff --git a/configure.ac b/configure.ac
index c0bb8a3..7130152 100644
--- a/configure.ac
+++ b/configure.ac
@@ -497,6 +497,12 @@ AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
AC_CHECK_FUNCS(setutxent utmpxname)
AC_CHECK_FUNCS(logout updwtmp logwtmp)
+# POSIX monotonic time
+OLDCFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=199309L"
+AC_CHECK_FUNCS(clock_gettime)
+CFLAGS="$OLDCFLAGS"
+
# OS X monotonic time
AC_CHECK_HEADERS([mach/mach_time.h])
AC_CHECK_FUNCS(mach_absolute_time)
diff --git a/dbutil.c b/dbutil.c
index 9f61a2b..32920f7 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -605,71 +605,67 @@ int constant_time_memcmp(const void* a, const void *b, size_t n)
return c;
}
-#if defined(__linux__) && defined(SYS_clock_gettime)
-/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to
-reach userspace include headers */
-#ifndef CLOCK_MONOTONIC_COARSE
-#define CLOCK_MONOTONIC_COARSE 6
-#endif
-/* Some old toolchains know SYS_clock_gettime but not CLOCK_MONOTONIC */
-#ifndef CLOCK_MONOTONIC
-#define CLOCK_MONOTONIC 1
-#endif
-static clockid_t get_linux_clock_source() {
- struct timespec ts;
- if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) {
- return CLOCK_MONOTONIC_COARSE;
- }
-
- if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) {
- return CLOCK_MONOTONIC;
- }
- return -1;
-}
-#endif
-
-time_t monotonic_now() {
+/* higher-resolution monotonic timestamp, falls back to gettimeofday */
+void gettime_wrapper(struct timespec *now) {
+ struct timeval tv;
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
/* time stands still when fuzzing */
- return 5;
+ now->tv_sec = 5;
+ now->tv_nsec = 0;
}
#endif
-#if defined(__linux__) && defined(SYS_clock_gettime)
- {
- static clockid_t clock_source = -2;
- if (clock_source == -2) {
- /* First run, find out which one works.
- -1 will fall back to time() */
- clock_source = get_linux_clock_source();
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+ /* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
+ if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
+ return;
}
+#endif
- if (clock_source >= 0) {
- struct timespec ts;
- if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) {
- /* Intermittent clock failures should not happen */
- dropbear_exit("Clock broke");
+#if defined(__linux__) && defined(SYS_clock_gettime)
+ {
+ /* Old linux toolchain - kernel might support it but not the build headers */
+ /* Also glibc <2.17 requires -lrt which we neglect to add */
+ static int linux_monotonic_failed = 0;
+ if (!linux_monotonic_failed) {
+ /* CLOCK_MONOTONIC isn't in some headers */
+ int clock_source_monotonic = 1;
+ if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
+ return;
+ } else {
+ /* Don't try again */
+ linux_monotonic_failed = 1;
}
- return ts.tv_sec;
}
}
-#endif /* linux clock_gettime */
+#endif /* linux fallback clock_gettime */
#if defined(HAVE_MACH_ABSOLUTE_TIME)
{
- /* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
+ /* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
static mach_timebase_info_data_t timebase_info;
+ uint64_t scaled_time;
if (timebase_info.denom == 0) {
mach_timebase_info(&timebase_info);
}
- return mach_absolute_time() * timebase_info.numer / timebase_info.denom
- / 1e9;
+ scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
+ now->tv_sec = scaled_time / 1000000000;
+ now->tv_nsec = scaled_time % 1000000000;
}
#endif /* osx mach_absolute_time */
/* Fallback for everything else - this will sometimes go backwards */
- return time(NULL);
+ gettimeofday(&tv, NULL);
+ now->tv_sec = tv.tv_sec;
+ now->tv_nsec = 1000*tv.tv_usec;
+}
+
+/* second-resolution monotonic timestamp */
+time_t monotonic_now() {
+ struct timespec ts;
+ gettime_wrapper(&ts);
+ return ts.tv_sec;
}
void fsync_parent_dir(const char* fn) {
diff --git a/dbutil.h b/dbutil.h
index 7cb9d68..2a1c82c 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -83,6 +83,8 @@ int constant_time_memcmp(const void* a, const void *b, size_t n);
/* Returns a time in seconds that doesn't go backwards - does not correspond to
a real-world clock */
time_t monotonic_now(void);
+/* Higher resolution clock_gettime(CLOCK_MONOTONIC) wrapper */
+void gettime_wrapper(struct timespec *now);
char * expand_homedir_path(const char *inpath);
diff --git a/includes.h b/includes.h
index 246882b..0f12620 100644
--- a/includes.h
+++ b/includes.h
@@ -29,6 +29,11 @@
#include "options.h"
#include "debug.h"
+#if __linux__
+/* For clock_gettime */
+#define _POSIX_C_SOURCE 199309L
+#endif
+
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/param.h> /* required for BSD4_4 define */
diff --git a/svr-auth.c b/svr-auth.c
index 10e51b3..f952b0b 100644
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -79,6 +79,9 @@ void recv_msg_userauth_request() {
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 @@ void send_msg_userauth_failure(int partial, int incrfail) {
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++;
}