=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/auth2.c,v retrieving revision 1.145 retrieving revision 1.146 diff -u -r1.145 -r1.146 --- src/usr.bin/ssh/auth2.c 2018/03/03 03:15:51 1.145 +++ src/usr.bin/ssh/auth2.c 2018/04/13 03:57:26 1.146 @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.145 2018/03/03 03:15:51 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.146 2018/04/13 03:57:26 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -54,6 +54,7 @@ #endif #include "monitor_wrap.h" #include "ssherr.h" +#include "digest.h" /* import */ extern ServerOptions options; @@ -200,6 +201,42 @@ return 0; } +#define MIN_FAIL_DELAY_SECONDS 0.005 +static double +user_specific_delay(const char *user) +{ + char b[512]; + size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512); + u_char *hash = xmalloc(len); + double delay; + + (void)snprintf(b, sizeof b, "%llu%s", options.timing_secret, user); + if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0) + fatal("%s: ssh_digest_memory", __func__); + /* 0-4.2 ms of delay */ + delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000; + freezero(hash, len); + debug3("%s: user specific delay %0.3lfms", __func__, delay/1000); + return MIN_FAIL_DELAY_SECONDS + delay; +} + +static void +ensure_minimum_time_since(double start, double seconds) +{ + struct timespec ts; + double elapsed = monotime_double() - start, req = seconds, remain; + + /* if we've already passed the requested time, scale up */ + while ((remain = seconds - elapsed) < 0.0) + seconds *= 2; + + ts.tv_sec = remain; + ts.tv_nsec = (remain - ts.tv_sec) * 1000000000; + debug3("%s: elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)", + __func__, elapsed*1000, remain*1000, req*1000); + nanosleep(&ts, NULL); +} + /*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) @@ -208,6 +245,7 @@ Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; + double tstart = monotime_double(); if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); @@ -269,6 +307,9 @@ debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(ssh); } + if (!authctxt->authenticated) + ensure_minimum_time_since(tstart, + user_specific_delay(authctxt->user)); userauth_finish(ssh, authenticated, method, NULL); free(service);