summaryrefslogtreecommitdiff
path: root/usr.bin/ssh/sshd.c
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2015-01-07 18:15:08 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2015-01-07 18:15:08 +0000
commit2bb425cd1ef2f49b2b6f49824d2e2e38ed9c6350 (patch)
tree604173a9f2c37cdb839a5babcf5343d1e216052e /usr.bin/ssh/sshd.c
parent581294aaaa2802272907f812f2e1979c88373eca (diff)
workaround for the Meyer, et al, Bleichenbacher Side Channel Attack.
fake up a bignum key before RSA decryption. discussed/ok djm markus
Diffstat (limited to 'usr.bin/ssh/sshd.c')
-rw-r--r--usr.bin/ssh/sshd.c104
1 files changed, 47 insertions, 57 deletions
diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c
index 5a98ea7652b..b2288f3795e 100644
--- a/usr.bin/ssh/sshd.c
+++ b/usr.bin/ssh/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.430 2014/12/22 07:55:51 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.431 2015/01/07 18:15:07 tedu Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2086,8 +2086,10 @@ do_ssh1_kex(void)
{
int i, len;
int rsafail = 0;
- BIGNUM *session_key_int;
+ BIGNUM *session_key_int, *fake_key_int, *real_key_int;
u_char session_key[SSH_SESSION_KEY_LENGTH];
+ u_char fake_key_bytes[4096 / 8];
+ size_t fake_key_len;
u_char cookie[8];
u_int cipher_type, auth_mask, protocol_flags;
@@ -2165,74 +2167,61 @@ do_ssh1_kex(void)
debug("Encryption type: %.200s", cipher_name(cipher_type));
/* Get the encrypted integer. */
- if ((session_key_int = BN_new()) == NULL)
+ if ((real_key_int = BN_new()) == NULL)
fatal("do_ssh1_kex: BN_new failed");
- packet_get_bignum(session_key_int);
+ packet_get_bignum(real_key_int);
protocol_flags = packet_get_int();
packet_set_protocol_flags(protocol_flags);
packet_check_eom();
- /* Decrypt session_key_int using host/server keys */
- rsafail = PRIVSEP(ssh1_session_key(session_key_int));
+ /* Setup a fake key in case RSA decryption fails */
+ if ((fake_key_int = BN_new()) == NULL)
+ fatal("do_ssh1_kex: BN_new failed");
+ fake_key_len = BN_num_bytes(real_key_int);
+ if (fake_key_len > sizeof(fake_key_bytes))
+ fake_key_len = sizeof(fake_key_bytes);
+ arc4random_buf(fake_key_bytes, fake_key_len);
+ if (BN_bin2bn(fake_key_bytes, fake_key_len, fake_key_int) == NULL)
+ fatal("do_ssh1_kex: BN_bin2bn failed");
+
+ /* Decrypt real_key_int using host/server keys */
+ rsafail = PRIVSEP(ssh1_session_key(real_key_int));
+ /* If decryption failed, use the fake key. Else, the real key. */
+ if (rsafail)
+ session_key_int = fake_key_int;
+ else
+ session_key_int = real_key_int;
/*
* Extract session key from the decrypted integer. The key is in the
* least significant 256 bits of the integer; the first byte of the
* key is in the highest bits.
*/
- if (!rsafail) {
- (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8);
- len = BN_num_bytes(session_key_int);
- if (len < 0 || (u_int)len > sizeof(session_key)) {
- error("do_ssh1_kex: bad session key len from %s: "
- "session_key_int %d > sizeof(session_key) %lu",
- get_remote_ipaddr(), len, (u_long)sizeof(session_key));
- rsafail++;
- } else {
- explicit_bzero(session_key, sizeof(session_key));
- BN_bn2bin(session_key_int,
- session_key + sizeof(session_key) - len);
-
- derive_ssh1_session_id(
- sensitive_data.ssh1_host_key->rsa->n,
- sensitive_data.server_key->rsa->n,
- cookie, session_id);
- /*
- * Xor the first 16 bytes of the session key with the
- * session id.
- */
- for (i = 0; i < 16; i++)
- session_key[i] ^= session_id[i];
- }
- }
- if (rsafail) {
- int bytes = BN_num_bytes(session_key_int);
- u_char *buf = xmalloc(bytes);
- struct ssh_digest_ctx *md;
-
- logit("do_connection: generating a fake encryption key");
- BN_bn2bin(session_key_int, buf);
- if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
- ssh_digest_update(md, buf, bytes) < 0 ||
- ssh_digest_update(md, sensitive_data.ssh1_cookie,
- SSH_SESSION_KEY_LENGTH) < 0 ||
- ssh_digest_final(md, session_key, sizeof(session_key)) < 0)
- fatal("%s: md5 failed", __func__);
- ssh_digest_free(md);
- if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
- ssh_digest_update(md, session_key, 16) < 0 ||
- ssh_digest_update(md, sensitive_data.ssh1_cookie,
- SSH_SESSION_KEY_LENGTH) < 0 ||
- ssh_digest_final(md, session_key + 16,
- sizeof(session_key) - 16) < 0)
- fatal("%s: md5 failed", __func__);
- ssh_digest_free(md);
- explicit_bzero(buf, bytes);
- free(buf);
+ (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8);
+ len = BN_num_bytes(session_key_int);
+ if (len < 0 || (u_int)len > sizeof(session_key)) {
+ error("do_ssh1_kex: bad session key len from %s: "
+ "session_key_int %d > sizeof(session_key) %lu",
+ get_remote_ipaddr(), len, (u_long)sizeof(session_key));
+ rsafail++;
+ } else {
+ explicit_bzero(session_key, sizeof(session_key));
+ BN_bn2bin(session_key_int,
+ session_key + sizeof(session_key) - len);
+
+ derive_ssh1_session_id(
+ sensitive_data.ssh1_host_key->rsa->n,
+ sensitive_data.server_key->rsa->n,
+ cookie, session_id);
+ /*
+ * Xor the first 16 bytes of the session key with the
+ * session id.
+ */
for (i = 0; i < 16; i++)
- session_id[i] = session_key[i] ^ session_key[i + 16];
+ session_key[i] ^= session_id[i];
}
+
/* Destroy the private and public keys. No longer. */
destroy_sensitive_data();
@@ -2240,7 +2229,8 @@ do_ssh1_kex(void)
mm_ssh1_session_id(session_id);
/* Destroy the decrypted integer. It is no longer needed. */
- BN_clear_free(session_key_int);
+ BN_clear_free(real_key_int);
+ BN_clear_free(fake_key_int);
/* Set the session key. From this on all communications will be encrypted. */
packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type);