# Date: Sun, 20 Apr 2014 17:14:08 +1000 (EST) # From: Damien Miller # To: openssh-unix-dev@mindrot.org # Subject: bad bignum encoding for curve25519-sha256@libssh.org # Message-ID: # # Hi, # # So I screwed up when writing the support for the curve25519 KEX method # that doesn't depend on OpenSSL's BIGNUM type - a bug in my code left # leading zero bytes where they should have been skipped. The impact of # this is that OpenSSH 6.5 and 6.6 will fail during key exchange with a # peer that implements curve25519-sha256@libssh.org properly about 0.2% # of the time (one in every 512ish connections). # # We've fixed this for OpenSSH 6.7 by avoiding the curve25519-sha256 # key exchange for previous versions, but I'd recommend distributors # of OpenSSH apply this patch so the affected code doesn't become # too entrenched in LTS releases. # # The patch fixes the bug and makes OpenSSH identify itself as 6.6.1 so as # to distinguish itself from the incorrect versions so the compatibility # code to disable the affected KEX isn't activated. # # I've committed this on the 6.6 branch too. # # Apologies for the hassle. # # -d diff --git a/openssh-6.6p1/bufaux.c b/openssh-6.6p1/bufaux.c --- a/openssh-6.6p1/bufaux.c +++ b/openssh-6.6p1/bufaux.c @@ -1,9 +1,9 @@ -/* $OpenBSD: bufaux.c,v 1.56 2014/02/02 03:44:31 djm Exp $ */ +/* $OpenBSD: bufaux.c,v 1.57 2014/04/16 23:22:45 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Auxiliary functions for storing and retrieving various data types to/from * Buffers. * * As far as I am concerned, the code I have written for this software @@ -367,16 +367,19 @@ buffer_get_bignum2_as_string(Buffer *buf void buffer_put_bignum2_from_string(Buffer *buffer, const u_char *s, u_int l) { u_char *buf, *p; int pad = 0; if (l > 8 * 1024) fatal("%s: length %u too long", __func__, l); + /* Skip leading zero bytes */ + for (; l > 0 && *s == 0; l--, s++) + ; p = buf = xmalloc(l + 1); /* * If most significant bit is set then prepend a zero byte to * avoid interpretation as a negative number. */ if (l > 0 && (s[0] & 0x80) != 0) { *p++ = '\0'; pad = 1; diff --git a/openssh-6.6p1/compat.c b/openssh-6.6p1/compat.c --- a/openssh-6.6p1/compat.c +++ b/openssh-6.6p1/compat.c @@ -90,16 +90,19 @@ compat_datafellows(const char *version) SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.*," "OpenSSH_3.0*," "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR }, { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, { "OpenSSH_4*", 0 }, { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT}, + { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH}, + { "OpenSSH_6.5*," + "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD}, { "OpenSSH*", SSH_NEW_OPENSSH }, { "*MindTerm*", 0 }, { "2.1.0*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| SSH_BUG_FIRSTKEX }, { "2.1 *", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| @@ -246,22 +249,34 @@ compat_cipher_proposal(char *cipher_prop debug2("%s: original cipher proposal: %s", __func__, cipher_prop); cipher_prop = filter_proposal(cipher_prop, "aes*"); debug2("%s: compat cipher proposal: %s", __func__, cipher_prop); if (*cipher_prop == '\0') fatal("No supported ciphers found"); return cipher_prop; } - char * compat_pkalg_proposal(char *pkalg_prop) { if (!(datafellows & SSH_BUG_RSASIGMD5)) return pkalg_prop; debug2("%s: original public key proposal: %s", __func__, pkalg_prop); pkalg_prop = filter_proposal(pkalg_prop, "ssh-rsa"); debug2("%s: compat public key proposal: %s", __func__, pkalg_prop); if (*pkalg_prop == '\0') fatal("No supported PK algorithms found"); return pkalg_prop; } +char * +compat_kex_proposal(char *kex_prop) +{ + if (!(datafellows & SSH_BUG_CURVE25519PAD)) + return kex_prop; + debug2("%s: original KEX proposal: %s", __func__, kex_prop); + kex_prop = filter_proposal(kex_prop, "curve25519-sha256@libssh.org"); + debug2("%s: compat KEX proposal: %s", __func__, kex_prop); + if (*kex_prop == '\0') + fatal("No supported key exchange algorithms found"); + return kex_prop; +} + diff --git a/openssh-6.6p1/compat.h b/openssh-6.6p1/compat.h --- a/openssh-6.6p1/compat.h +++ b/openssh-6.6p1/compat.h @@ -54,20 +54,22 @@ #define SSH_BUG_DUMMYCHAN 0x00100000 #define SSH_BUG_EXTEOF 0x00200000 #define SSH_BUG_PROBE 0x00400000 #define SSH_BUG_FIRSTKEX 0x00800000 #define SSH_OLD_FORWARD_ADDR 0x01000000 #define SSH_BUG_RFWD_ADDR 0x02000000 #define SSH_NEW_OPENSSH 0x04000000 #define SSH_BUG_DYNAMIC_RPORT 0x08000000 +#define SSH_BUG_CURVE25519PAD 0x10000000 void enable_compat13(void); void enable_compat20(void); void compat_datafellows(const char *); int proto_spec(const char *); char *compat_cipher_proposal(char *); char *compat_pkalg_proposal(char *); +char *compat_kex_proposal(char *); extern int compat13; extern int compat20; extern int datafellows; #endif diff --git a/openssh-6.6p1/sshconnect2.c b/openssh-6.6p1/sshconnect2.c --- a/openssh-6.6p1/sshconnect2.c +++ b/openssh-6.6p1/sshconnect2.c @@ -190,16 +190,18 @@ ssh_kex2(char *host, struct sockaddr *ho else { /* Prefer algorithms that we already have keys for */ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( order_hostkeyalgs(host, hostaddr, port)); } if (options.kex_algorithms != NULL) myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( + myproposal[PROPOSAL_KEX_ALGS]); if (options.rekey_limit || options.rekey_interval) packet_set_rekey_limits((u_int32_t)options.rekey_limit, (time_t)options.rekey_interval); /* start key exchange */ kex = kex_setup(myproposal); kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; diff --git a/openssh-6.6p1/sshd.c b/openssh-6.6p1/sshd.c --- a/openssh-6.6p1/sshd.c +++ b/openssh-6.6p1/sshd.c @@ -2457,16 +2457,19 @@ do_ssh2_kex(void) myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } else if (options.compression == COMP_DELAYED) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com"; } if (options.kex_algorithms != NULL) myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( + myproposal[PROPOSAL_KEX_ALGS]); + if (options.rekey_limit || options.rekey_interval) packet_set_rekey_limits((u_int32_t)options.rekey_limit, (time_t)options.rekey_interval); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( list_hostkey_types()); /* start key exchange */ diff --git a/openssh-6.6p1/version.h b/openssh-6.6p1/version.h --- a/openssh-6.6p1/version.h +++ b/openssh-6.6p1/version.h @@ -1,6 +1,6 @@ /* $OpenBSD: version.h,v 1.70 2014/02/27 22:57:40 djm Exp $ */ -#define SSH_VERSION "OpenSSH_6.6" +#define SSH_VERSION "OpenSSH_6.6.1" #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE