diff options
author | dtucker@openbsd.org <dtucker@openbsd.org> | 2018-04-13 03:57:26 +0000 |
---|---|---|
committer | Darren Tucker <dtucker@dtucker.net> | 2018-04-13 15:26:11 +1000 |
commit | e9d910b0289c820852f7afa67f584cef1c05fe95 (patch) | |
tree | 207f618329c9df13a2278c71c95b1dc66450bb86 /auth2.c | |
parent | d97874cbd909eb706886cd0cdd418f812c119ef9 (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.c | 43 |
1 files changed, 42 insertions, 1 deletions
@@ -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 */ |
62 | extern ServerOptions options; | 63 | extern 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 | ||
215 | static double | ||
216 | user_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 | |||
233 | static void | ||
234 | ensure_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*/ |
214 | static int | 251 | static int |
215 | input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) | 252 | input_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); |