summaryrefslogtreecommitdiff
path: root/auth2.c
diff options
context:
space:
mode:
authordtucker@openbsd.org <dtucker@openbsd.org>2018-04-13 03:57:26 +0000
committerDarren Tucker <dtucker@dtucker.net>2018-04-13 15:26:11 +1000
commite9d910b0289c820852f7afa67f584cef1c05fe95 (patch)
tree207f618329c9df13a2278c71c95b1dc66450bb86 /auth2.c
parentd97874cbd909eb706886cd0cdd418f812c119ef9 (diff)
upstream: Defend against user enumeration timing attacks. This
establishes a minimum time for each failed authentication attempt (5ms) and adds a per-user constant derived from a host secret (0-4ms). Based on work by joona.kannisto at tut.fi, ok markus@ djm@. OpenBSD-Commit-ID: b7845b355bb7381703339c8fb0e57e81a20ae5ca
Diffstat (limited to 'auth2.c')
-rw-r--r--auth2.c43
1 files changed, 42 insertions, 1 deletions
diff --git a/auth2.c b/auth2.c
index e0034229a..2246e54d5 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth2.c,v 1.145 2018/03/03 03:15:51 djm Exp $ */ 1/* $OpenBSD: auth2.c,v 1.146 2018/04/13 03:57:26 dtucker Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -57,6 +57,7 @@
57#endif 57#endif
58#include "monitor_wrap.h" 58#include "monitor_wrap.h"
59#include "ssherr.h" 59#include "ssherr.h"
60#include "digest.h"
60 61
61/* import */ 62/* import */
62extern ServerOptions options; 63extern ServerOptions options;
@@ -210,6 +211,42 @@ input_service_request(int type, u_int32_t seq, struct ssh *ssh)
210 return 0; 211 return 0;
211} 212}
212 213
214#define MIN_FAIL_DELAY_SECONDS 0.005
215static double
216user_specific_delay(const char *user)
217{
218 char b[512];
219 size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512);
220 u_char *hash = xmalloc(len);
221 double delay;
222
223 (void)snprintf(b, sizeof b, "%llu%s", options.timing_secret, user);
224 if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0)
225 fatal("%s: ssh_digest_memory", __func__);
226 /* 0-4.2 ms of delay */
227 delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000;
228 freezero(hash, len);
229 debug3("%s: user specific delay %0.3lfms", __func__, delay/1000);
230 return MIN_FAIL_DELAY_SECONDS + delay;
231}
232
233static void
234ensure_minimum_time_since(double start, double seconds)
235{
236 struct timespec ts;
237 double elapsed = monotime_double() - start, req = seconds, remain;
238
239 /* if we've already passed the requested time, scale up */
240 while ((remain = seconds - elapsed) < 0.0)
241 seconds *= 2;
242
243 ts.tv_sec = remain;
244 ts.tv_nsec = (remain - ts.tv_sec) * 1000000000;
245 debug3("%s: elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)",
246 __func__, elapsed*1000, remain*1000, req*1000);
247 nanosleep(&ts, NULL);
248}
249
213/*ARGSUSED*/ 250/*ARGSUSED*/
214static int 251static int
215input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) 252input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
@@ -218,6 +255,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
218 Authmethod *m = NULL; 255 Authmethod *m = NULL;
219 char *user, *service, *method, *style = NULL; 256 char *user, *service, *method, *style = NULL;
220 int authenticated = 0; 257 int authenticated = 0;
258 double tstart = monotime_double();
221 259
222 if (authctxt == NULL) 260 if (authctxt == NULL)
223 fatal("input_userauth_request: no authctxt"); 261 fatal("input_userauth_request: no authctxt");
@@ -286,6 +324,9 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
286 debug2("input_userauth_request: try method %s", method); 324 debug2("input_userauth_request: try method %s", method);
287 authenticated = m->userauth(ssh); 325 authenticated = m->userauth(ssh);
288 } 326 }
327 if (!authctxt->authenticated)
328 ensure_minimum_time_since(tstart,
329 user_specific_delay(authctxt->user));
289 userauth_finish(ssh, authenticated, method, NULL); 330 userauth_finish(ssh, authenticated, method, NULL);
290 331
291 free(service); 332 free(service);