diff options
-rw-r--r-- | auth2.c | 43 | ||||
-rw-r--r-- | servconf.h | 3 | ||||
-rw-r--r-- | sshd.c | 41 |
3 files changed, 84 insertions, 3 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); |
diff --git a/servconf.h b/servconf.h index 37a0fb1a3..6d2553c38 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.h,v 1.130 2017/10/25 00:19:47 djm Exp $ */ | 1 | /* $OpenBSD: servconf.h,v 1.131 2018/04/13 03:57:26 dtucker Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -209,6 +209,7 @@ typedef struct { | |||
209 | 209 | ||
210 | int fingerprint_hash; | 210 | int fingerprint_hash; |
211 | int expose_userauth_info; | 211 | int expose_userauth_info; |
212 | u_int64_t timing_secret; | ||
212 | } ServerOptions; | 213 | } ServerOptions; |
213 | 214 | ||
214 | /* Information about the incoming connection as used by Match */ | 215 | /* Information about the incoming connection as used by Match */ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshd.c,v 1.507 2018/04/10 00:10:49 djm Exp $ */ | 1 | /* $OpenBSD: sshd.c,v 1.508 2018/04/13 03:57:26 dtucker Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -1413,6 +1413,43 @@ set_process_rdomain(struct ssh *ssh, const char *name) | |||
1413 | #endif | 1413 | #endif |
1414 | } | 1414 | } |
1415 | 1415 | ||
1416 | static void | ||
1417 | accumulate_host_timing_secret(struct sshbuf *server_cfg, | ||
1418 | const struct sshkey *key) | ||
1419 | { | ||
1420 | static struct ssh_digest_ctx *ctx; | ||
1421 | u_char *hash; | ||
1422 | size_t len; | ||
1423 | struct sshbuf *buf; | ||
1424 | int r; | ||
1425 | |||
1426 | if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL) | ||
1427 | fatal("%s: ssh_digest_start", __func__); | ||
1428 | if (key == NULL) { /* finalize */ | ||
1429 | /* add server config in case we are using agent for host keys */ | ||
1430 | if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg), | ||
1431 | sshbuf_len(server_cfg)) != 0) | ||
1432 | fatal("%s: ssh_digest_update", __func__); | ||
1433 | len = ssh_digest_bytes(SSH_DIGEST_SHA512); | ||
1434 | hash = xmalloc(len); | ||
1435 | if (ssh_digest_final(ctx, hash, len) != 0) | ||
1436 | fatal("%s: ssh_digest_final", __func__); | ||
1437 | options.timing_secret = PEEK_U64(hash); | ||
1438 | freezero(hash, len); | ||
1439 | ssh_digest_free(ctx); | ||
1440 | ctx = NULL; | ||
1441 | return; | ||
1442 | } | ||
1443 | if ((buf = sshbuf_new()) == NULL) | ||
1444 | fatal("%s could not allocate buffer", __func__); | ||
1445 | if ((r = sshkey_private_serialize(key, buf)) != 0) | ||
1446 | fatal("sshkey_private_serialize: %s", ssh_err(r)); | ||
1447 | if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0) | ||
1448 | fatal("%s: ssh_digest_update", __func__); | ||
1449 | sshbuf_reset(buf); | ||
1450 | sshbuf_free(buf); | ||
1451 | } | ||
1452 | |||
1416 | /* | 1453 | /* |
1417 | * Main program for the daemon. | 1454 | * Main program for the daemon. |
1418 | */ | 1455 | */ |
@@ -1728,6 +1765,7 @@ main(int ac, char **av) | |||
1728 | keytype = pubkey->type; | 1765 | keytype = pubkey->type; |
1729 | } else if (key != NULL) { | 1766 | } else if (key != NULL) { |
1730 | keytype = key->type; | 1767 | keytype = key->type; |
1768 | accumulate_host_timing_secret(&cfg, key); | ||
1731 | } else { | 1769 | } else { |
1732 | error("Could not load host key: %s", | 1770 | error("Could not load host key: %s", |
1733 | options.host_key_files[i]); | 1771 | options.host_key_files[i]); |
@@ -1753,6 +1791,7 @@ main(int ac, char **av) | |||
1753 | key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); | 1791 | key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); |
1754 | free(fp); | 1792 | free(fp); |
1755 | } | 1793 | } |
1794 | accumulate_host_timing_secret(&cfg, NULL); | ||
1756 | if (!sensitive_data.have_ssh2_key) { | 1795 | if (!sensitive_data.have_ssh2_key) { |
1757 | logit("sshd: no hostkeys available -- exiting."); | 1796 | logit("sshd: no hostkeys available -- exiting."); |
1758 | exit(1); | 1797 | exit(1); |