mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-12-22 10:00:14 +00:00
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
This commit is contained in:
parent
d97874cbd9
commit
e9d910b028
43
auth2.c
43
auth2.c
@ -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.
|
||||
*
|
||||
@ -57,6 +57,7 @@
|
||||
#endif
|
||||
#include "monitor_wrap.h"
|
||||
#include "ssherr.h"
|
||||
#include "digest.h"
|
||||
|
||||
/* import */
|
||||
extern ServerOptions options;
|
||||
@ -210,6 +211,42 @@ input_service_request(int type, u_int32_t seq, struct ssh *ssh)
|
||||
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)
|
||||
@ -218,6 +255,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
|
||||
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");
|
||||
@ -286,6 +324,9 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
|
||||
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);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: servconf.h,v 1.130 2017/10/25 00:19:47 djm Exp $ */
|
||||
/* $OpenBSD: servconf.h,v 1.131 2018/04/13 03:57:26 dtucker Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -209,6 +209,7 @@ typedef struct {
|
||||
|
||||
int fingerprint_hash;
|
||||
int expose_userauth_info;
|
||||
u_int64_t timing_secret;
|
||||
} ServerOptions;
|
||||
|
||||
/* Information about the incoming connection as used by Match */
|
||||
|
41
sshd.c
41
sshd.c
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshd.c,v 1.507 2018/04/10 00:10:49 djm Exp $ */
|
||||
/* $OpenBSD: sshd.c,v 1.508 2018/04/13 03:57:26 dtucker Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -1413,6 +1413,43 @@ set_process_rdomain(struct ssh *ssh, const char *name)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
accumulate_host_timing_secret(struct sshbuf *server_cfg,
|
||||
const struct sshkey *key)
|
||||
{
|
||||
static struct ssh_digest_ctx *ctx;
|
||||
u_char *hash;
|
||||
size_t len;
|
||||
struct sshbuf *buf;
|
||||
int r;
|
||||
|
||||
if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL)
|
||||
fatal("%s: ssh_digest_start", __func__);
|
||||
if (key == NULL) { /* finalize */
|
||||
/* add server config in case we are using agent for host keys */
|
||||
if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg),
|
||||
sshbuf_len(server_cfg)) != 0)
|
||||
fatal("%s: ssh_digest_update", __func__);
|
||||
len = ssh_digest_bytes(SSH_DIGEST_SHA512);
|
||||
hash = xmalloc(len);
|
||||
if (ssh_digest_final(ctx, hash, len) != 0)
|
||||
fatal("%s: ssh_digest_final", __func__);
|
||||
options.timing_secret = PEEK_U64(hash);
|
||||
freezero(hash, len);
|
||||
ssh_digest_free(ctx);
|
||||
ctx = NULL;
|
||||
return;
|
||||
}
|
||||
if ((buf = sshbuf_new()) == NULL)
|
||||
fatal("%s could not allocate buffer", __func__);
|
||||
if ((r = sshkey_private_serialize(key, buf)) != 0)
|
||||
fatal("sshkey_private_serialize: %s", ssh_err(r));
|
||||
if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0)
|
||||
fatal("%s: ssh_digest_update", __func__);
|
||||
sshbuf_reset(buf);
|
||||
sshbuf_free(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program for the daemon.
|
||||
*/
|
||||
@ -1728,6 +1765,7 @@ main(int ac, char **av)
|
||||
keytype = pubkey->type;
|
||||
} else if (key != NULL) {
|
||||
keytype = key->type;
|
||||
accumulate_host_timing_secret(&cfg, key);
|
||||
} else {
|
||||
error("Could not load host key: %s",
|
||||
options.host_key_files[i]);
|
||||
@ -1753,6 +1791,7 @@ main(int ac, char **av)
|
||||
key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp);
|
||||
free(fp);
|
||||
}
|
||||
accumulate_host_timing_secret(&cfg, NULL);
|
||||
if (!sensitive_data.have_ssh2_key) {
|
||||
logit("sshd: no hostkeys available -- exiting.");
|
||||
exit(1);
|
||||
|
Loading…
Reference in New Issue
Block a user