From 318076a1838c0e270afc5ac5a8081b3a1273cfb4 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 25 Apr 2015 10:46:53 +0100 Subject: [PATCH] Support RFC 4419. PuTTY now uses the updated version of Diffie-Hellman group exchange, except for a few old OpenSSH versions which Darren Tucker reports only support the old version. FIXME: this needs further work because the Bugs config panel has now overflowed. (cherry picked from commit 62a1bce7cb3ecb98feb57c7f1fd5d55845ce1533) --- config.c | 3 +++ doc/config.but | 17 +++++++++++++++++ putty.h | 1 + settings.c | 2 ++ ssh.c | 35 +++++++++++++++++++++++++++++++++-- ssh.h | 3 ++- windows/winhelp.h | 1 + 7 files changed, 59 insertions(+), 3 deletions(-) diff --git a/config.c b/config.c index c2acd628..395f9c81 100644 --- a/config.c +++ b/config.c @@ -2603,6 +2603,9 @@ void setup_config_box(struct controlbox *b, int midsession, ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, HELPCTX(ssh_bugs_maxpkt2), sshbug_handler, I(CONF_sshbug_maxpkt2)); + ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20, + HELPCTX(ssh_bugs_oldgex2), + sshbug_handler, I(CONF_sshbug_oldgex2)); ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20, HELPCTX(ssh_bugs_chanreq), sshbug_handler, I(CONF_sshbug_chanreq)); diff --git a/doc/config.but b/doc/config.but index 0f8175f2..1d81460f 100644 --- a/doc/config.but +++ b/doc/config.but @@ -3386,6 +3386,23 @@ reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of \q{Received \cw{SSH2_MSG_CHANNEL_FAILURE} for nonexistent channel 256}. +\S{config-ssh-bug-oldgex2} \q{Only supports pre-RFC4419 SSH-2 DH GEX} + +\cfg{winhelp-topic}{ssh.bugs.oldgex2} + +The SSH key exchange method that uses Diffie-Hellman group exchange +was redesigned after its original release, to use a slightly more +sophisticated setup message. Almost all SSH implementations switched +over to the new version. (PuTTY was one of the last.) A few old +servers still only support the old one. + +If this bug is detected, and the client and server negotiate +Diffie-Hellman group exchange, then PuTTY will send the old message +now known as \cw{SSH2_MSG_KEX_DH_GEX_REQUEST_OLD} in place of the new +\cw{SSH2_MSG_KEX_DH_GEX_REQUEST}. + +This is an SSH-2-specific bug. + \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply diff --git a/putty.h b/putty.h index 5e043960..e030df79 100644 --- a/putty.h +++ b/putty.h @@ -837,6 +837,7 @@ void cleanup_exit(int); X(INT, NONE, sshbug_rekey2) \ X(INT, NONE, sshbug_maxpkt2) \ X(INT, NONE, sshbug_ignore2) \ + X(INT, NONE, sshbug_oldgex2) \ X(INT, NONE, sshbug_winadj) \ X(INT, NONE, sshbug_chanreq) \ /* \ diff --git a/settings.c b/settings.c index a7a88785..02d7506b 100644 --- a/settings.c +++ b/settings.c @@ -630,6 +630,7 @@ void save_open_settings(void *sesskey, Conf *conf) write_setting_i(sesskey, "BugPKSessID2", 2-conf_get_int(conf, CONF_sshbug_pksessid2)); write_setting_i(sesskey, "BugRekey2", 2-conf_get_int(conf, CONF_sshbug_rekey2)); write_setting_i(sesskey, "BugMaxPkt2", 2-conf_get_int(conf, CONF_sshbug_maxpkt2)); + write_setting_i(sesskey, "BugOldGex2", 2-conf_get_int(conf, CONF_sshbug_oldgex2)); write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); write_setting_i(sesskey, "BugChanReq", 2-conf_get_int(conf, CONF_sshbug_chanreq)); write_setting_i(sesskey, "StampUtmp", conf_get_int(conf, CONF_stamp_utmp)); @@ -977,6 +978,7 @@ void load_open_settings(void *sesskey, Conf *conf) i = gppi_raw(sesskey, "BugPKSessID2", 0); conf_set_int(conf, CONF_sshbug_pksessid2, 2-i); i = gppi_raw(sesskey, "BugRekey2", 0); conf_set_int(conf, CONF_sshbug_rekey2, 2-i); i = gppi_raw(sesskey, "BugMaxPkt2", 0); conf_set_int(conf, CONF_sshbug_maxpkt2, 2-i); + i = gppi_raw(sesskey, "BugOldGex2", 0); conf_set_int(conf, CONF_sshbug_oldgex2, 2-i); i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); i = gppi_raw(sesskey, "BugChanReq", 0); conf_set_int(conf, CONF_sshbug_chanreq, 2-i); conf_set_int(conf, CONF_ssh_simple, FALSE); diff --git a/ssh.c b/ssh.c index 13b67b6b..735bc174 100644 --- a/ssh.c +++ b/ssh.c @@ -76,6 +76,10 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_CHOKES_ON_SSH2_IGNORE 512 #define BUG_CHOKES_ON_WINADJ 1024 #define BUG_SENDS_LATE_REQUEST_REPLY 2048 +#define BUG_SSH2_OLDGEX 4096 + +#define DH_MIN_SIZE 1024 +#define DH_MAX_SIZE 8192 /* * Codes for terminal modes. @@ -247,6 +251,7 @@ static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) translate(SSH2_MSG_NEWKEYS); translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP); translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP); + translatek(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, SSH2_PKTCTX_DHGEX); translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX); translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX); translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX); @@ -2805,6 +2810,17 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) logevent("We believe remote version has SSH-2 ignore bug"); } + if (conf_get_int(ssh->conf, CONF_sshbug_oldgex2) == FORCE_ON || + (conf_get_int(ssh->conf, CONF_sshbug_oldgex2) == AUTO && + (wc_match("OpenSSH_2.[235]*", imp)))) { + /* + * These versions only support the original (pre-RFC4419) + * SSH-2 GEX request. + */ + ssh->remote_bugs |= BUG_SSH2_OLDGEX; + logevent("We believe remote version has outdated SSH-2 GEX"); + } + if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) { /* * Servers that don't support our winadj request for one @@ -6595,8 +6611,19 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, * much data. */ s->pbits = 512 << ((s->nbits - 1) / 64); - s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST); - ssh2_pkt_adduint32(s->pktout, s->pbits); + if (s->pbits < DH_MIN_SIZE) + s->pbits = DH_MIN_SIZE; + if (s->pbits > DH_MAX_SIZE) + s->pbits = DH_MAX_SIZE; + if ((ssh->remote_bugs & BUG_SSH2_OLDGEX)) { + s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); + ssh2_pkt_adduint32(s->pktout, s->pbits); + } else { + s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST); + ssh2_pkt_adduint32(s->pktout, DH_MIN_SIZE); + ssh2_pkt_adduint32(s->pktout, s->pbits); + ssh2_pkt_adduint32(s->pktout, DH_MAX_SIZE); + } ssh2_pkt_send_noqueue(ssh, s->pktout); crWaitUntilV(pktin); @@ -6664,7 +6691,11 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen); if (!ssh->kex->pdata) { + if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX)) + hash_uint32(ssh->kex->hash, ssh->exhash, DH_MIN_SIZE); hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits); + if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX)) + hash_uint32(ssh->kex->hash, ssh->exhash, DH_MAX_SIZE); hash_mpint(ssh->kex->hash, ssh->exhash, s->p); hash_mpint(ssh->kex->hash, ssh->exhash, s->g); } diff --git a/ssh.h b/ssh.h index 52500ac8..e50fbca3 100644 --- a/ssh.h +++ b/ssh.h @@ -720,7 +720,8 @@ void platform_ssh_share_cleanup(const char *name); #define SSH2_MSG_NEWKEYS 21 /* 0x15 */ #define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */ #define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */ -#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */ +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 /* 0x1e */ +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 /* 0x1e */ #define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */ #define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */ #define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */ diff --git a/windows/winhelp.h b/windows/winhelp.h index fc10a8f4..9b21c92c 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -148,6 +148,7 @@ #define WINHELP_CTX_ssh_bugs_maxpkt2 "ssh.bugs.maxpkt2:config-ssh-bug-maxpkt2" #define WINHELP_CTX_ssh_bugs_winadj "ssh.bugs.winadj:config-ssh-bug-winadj" #define WINHELP_CTX_ssh_bugs_chanreq "ssh.bugs.winadj:config-ssh-bug-chanreq" +#define WINHELP_CTX_ssh_bugs_oldgex2 "ssh.bugs.oldgex2:config-ssh-bug-oldgex2" #define WINHELP_CTX_serial_line "serial.line:config-serial-line" #define WINHELP_CTX_serial_speed "serial.speed:config-serial-speed" #define WINHELP_CTX_serial_databits "serial.databits:config-serial-databits" -- 2.45.2