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 /sshd.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 'sshd.c')
-rw-r--r-- | sshd.c | 41 |
1 files changed, 40 insertions, 1 deletions
@@ -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); |