From 16dfefcbdedb00354860adb32034ac0f3791833c Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Mon, 28 Mar 2016 20:23:57 +0100 Subject: [PATCH] Stop supporting fallback between SSH versions. The UI now only has "1" and "2" options for SSH protocol version, which behave like the old "1 only" and "2 only" options; old SSH-N-with-fallback settings are interpreted as SSH-N-only. This prevents any attempt at a protocol downgrade attack. Most users should see no difference; those poor souls who still have to work with SSH-1 equipment now have to explicitly opt in. --- config.c | 8 +++----- doc/config.but | 36 +++++++++++++++++------------------- doc/errors.but | 19 +++++++++++++++++++ doc/faq.but | 6 +++++- doc/index.but | 1 - doc/using.but | 5 ++--- putty.h | 15 ++++++++++++++- settings.c | 11 +++++++++-- ssh.c | 32 +++++++++++++++++++++----------- 9 files changed, 90 insertions(+), 43 deletions(-) diff --git a/config.c b/config.c index b32cfae5..328f39e8 100644 --- a/config.c +++ b/config.c @@ -2248,14 +2248,12 @@ void setup_config_box(struct controlbox *b, int midsession, if (!midsession) { s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options"); - ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4, + ctrl_radiobuttons(s, "SSH protocol version:", NO_SHORTCUT, 2, HELPCTX(ssh_protocol), conf_radiobutton_handler, I(CONF_sshprot), - "1 only", 'l', I(0), - "1", '1', I(1), - "2", '2', I(2), - "2 only", 'y', I(3), NULL); + "2", '2', I(3), + "1 (INSECURE)", '1', I(0), NULL); } /* diff --git a/doc/config.but b/doc/config.but index aa2f5272..94626ab0 100644 --- a/doc/config.but +++ b/doc/config.but @@ -1667,7 +1667,7 @@ Keepalives are only supported in Telnet and SSH; the Rlogin and Raw protocols offer no way of implementing them. (For an alternative, see \k{config-tcp-keepalives}.) -Note that if you are using \i{SSH-1} and the server has a bug that makes +Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see \k{config-ssh-bug-ignore1}), enabling keepalives will have no effect. @@ -2267,30 +2267,28 @@ client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-\i{bandwidth} connection. -\S{config-ssh-prot} \q{Preferred \i{SSH protocol version}} +\S{config-ssh-prot} \q{\i{SSH protocol version}} \cfg{winhelp-topic}{ssh.protocol} -This allows you to select whether you would prefer to use \i{SSH protocol -version 1} or \I{SSH-2}version 2, and whether to permit falling back -to the other version. +This allows you to select whether to use \i{SSH protocol version 2} +or the older \I{SSH-1}version 1. -With the settings \q{1} and \q{2}, PuTTY will attempt to use protocol 1 -if the server you connect to does not offer protocol 2, and vice versa. +You should normally leave this at the default of \q{2}. As well as +having fewer features, the older SSH-1 protocol is no longer +developed, has many known cryptographic weaknesses, and is generally +not considered to be secure. PuTTY's protocol 1 implementation is +provided mainly for compatibility, and is no longer being enhanced. -If you select \q{1 only} or \q{2 only} here, PuTTY will only connect -if the server you connect to offers the SSH protocol version you -have specified. +If a server offers both versions, prefer \q{2}. If you have some +server or piece of equipment that only talks SSH-1, select \q{1} +here, and do not treat the resulting connection as secure. -You should normally leave this at the default, \q{2 only}. The older -SSH-1 protocol is no longer developed, has many known cryptographic -weaknesses, and is generally not considered to be secure. If you -permit use of SSH-1 by selecting \q{2} instead of \q{2 only}, an -active attacker can force downgrade to SSH-1 even if the server -you're connecting to supports SSH-2. - -PuTTY's protocol 1 implementation is provided mainly for -compatibility, and is no longer being enhanced. +PuTTY will not automatically fall back to the other version of the +protocol if the server turns out not to match your selection here; +instead, it will put up an error message and abort the connection. +This prevents an active attacker downgrading an intended SSH-2 +connection to SSH-1. \S{config-ssh-sharing} Sharing an SSH connection between PuTTY tools diff --git a/doc/errors.but b/doc/errors.but index bf9672d0..e221a2d9 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -56,6 +56,25 @@ in the same way as you would if it was new. See \k{gs-hostkey} for more information on host keys. +\H{errors-ssh-protocol} \q{SSH protocol version 2 required by our +configuration but server only provides (old, insecure) SSH-1} + +By default, PuTTY only supports connecting to SSH servers that +implement \i{SSH protocol version 2}. If you see this message, the +server you're trying to connect to only supports the older SSH-1 +protocol. + +If the server genuinely only supports SSH-1, then you need to either +change the \q{SSH protocol version} setting (see \k{config-ssh-prot}), +or use the \c{-1} command-line option; in any case, you should not +treat the resulting connection as secure. + +You might start seeing this message with new versions of PuTTY +\#{XXX-REVIEW-BEFORE-RELEASE: (from 0.XX onwards)} +where you didn't before, because it used to be possible to configure +PuTTY to automatically fall back from SSH-2 to SSH-1. This is no +longer supported, to prevent the possibility of a downgrade attack. + \H{errors-cipher-warning} \q{The first cipher supported by the server is ... below the configured warning threshold} diff --git a/doc/faq.but b/doc/faq.but index af69f50c..040d61e7 100644 --- a/doc/faq.but +++ b/doc/faq.but @@ -64,7 +64,11 @@ files into PuTTY's format. Yes. SSH-1 support has always been available in PuTTY. However, the SSH-1 protocol has many weaknesses and is no longer -considered secure; it should be avoided if at all possible. +considered secure; you should use SSH-2 instead if at all possible. + +\#{XXX-REVIEW-BEFORE-RELEASE: +As of 0.68, PuTTY will no longer fall back to SSH-1 if the server +doesn't appear to support SSH-2; you must explicitly ask for SSH-1. } \S{faq-localecho}{Question} Does PuTTY support \i{local echo}? diff --git a/doc/index.but b/doc/index.but index 5f497bbf..0375164b 100644 --- a/doc/index.but +++ b/doc/index.but @@ -59,7 +59,6 @@ from SSH and Telnet \IM{security hazard}{security risk} security hazard -\IM{SSH-1}{SSH protocol version 1} SSH-1 \IM{SSH-2}{SSH protocol version 2} SSH-2 \IM{terminal window}{PuTTY window} terminal window diff --git a/doc/using.but b/doc/using.but index b70d0e85..fc5859cf 100644 --- a/doc/using.but +++ b/doc/using.but @@ -900,9 +900,8 @@ The \c{-1} and \c{-2} options force PuTTY to use version \I{SSH-1}1 or version \I{SSH-2}2 of the SSH protocol. These options are only meaningful if you are using SSH. -These options are equivalent to selecting your preferred SSH -protocol version as \q{1 only} or \q{2 only} in the SSH panel of the -PuTTY configuration box (see \k{config-ssh-prot}). +These options are equivalent to selecting the SSH protocol version in +the SSH panel of the PuTTY configuration box (see \k{config-ssh-prot}). \S2{using-cmdline-ipversion} \i\c{-4} and \i\c{-6}: specify an \i{Internet protocol version} diff --git a/putty.h b/putty.h index a149edb1..b06ff898 100644 --- a/putty.h +++ b/putty.h @@ -715,7 +715,20 @@ void cleanup_exit(int); X(INT, NONE, change_username) /* allow username switching in SSH-2 */ \ X(INT, INT, ssh_cipherlist) \ X(FILENAME, NONE, keyfile) \ - X(INT, NONE, sshprot) /* use v1 or v2 when both available */ \ + /* \ + * Which SSH protocol to use. \ + * For historical reasons, the current legal values for CONF_sshprot \ + * are: \ + * 0 = SSH-1 only \ + * 3 = SSH-2 only \ + * We used to also support \ + * 1 = SSH-1 with fallback to SSH-2 \ + * 2 = SSH-2 with fallback to SSH-1 \ + * and we continue to use 0/3 in storage formats rather than the more \ + * obvious 1/2 to avoid surprises if someone saves a session and later \ + * downgrades PuTTY. So it's easier to use these numbers internally too. \ + */ \ + X(INT, NONE, sshprot) \ X(INT, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ X(INT, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ diff --git a/settings.c b/settings.c index b6146960..a5d29748 100644 --- a/settings.c +++ b/settings.c @@ -803,8 +803,15 @@ void load_open_settings(void *sesskey, Conf *conf) hknames, HK_MAX, conf, CONF_ssh_hklist); gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); - /* SSH-2 only by default */ - gppi(sesskey, "SshProt", 3, conf, CONF_sshprot); + { + /* SSH-2 only by default */ + int sshprot = gppi_raw(sesskey, "SshProt", 3); + /* Old sessions may contain the values correponding to the fallbacks + * we used to allow; migrate them */ + if (sshprot == 1) sshprot = 0; /* => "SSH-1 only" */ + else if (sshprot == 2) sshprot = 3; /* => "SSH-2 only" */ + conf_set_int(conf, CONF_sshprot, sshprot); + } gpps(sesskey, "LogHost", "", conf, CONF_loghost); gppi(sesskey, "SSH2DES", 0, conf, CONF_ssh2_des_cbc); gppi(sesskey, "SshNoAuth", 0, conf, CONF_ssh_no_userauth); diff --git a/ssh.c b/ssh.c index 146853dd..cc8832fd 100644 --- a/ssh.c +++ b/ssh.c @@ -3138,15 +3138,21 @@ static int do_ssh_init(Ssh ssh, unsigned char c) /* Anything greater or equal to "1.99" means protocol 2 is supported. */ s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0; - if (conf_get_int(ssh->conf, CONF_sshprot) == 0 && !s->proto1) { - bombout(("SSH protocol version 1 required by configuration but " - "not provided by server")); - crStop(0); - } - if (conf_get_int(ssh->conf, CONF_sshprot) == 3 && !s->proto2) { - bombout(("SSH protocol version 2 required by configuration but " - "not provided by server")); - crStop(0); + if (conf_get_int(ssh->conf, CONF_sshprot) == 0) { + if (!s->proto1) { + bombout(("SSH protocol version 1 required by our configuration " + "but not provided by server")); + crStop(0); + } + } else if (conf_get_int(ssh->conf, CONF_sshprot) == 3) { + if (!s->proto2) { + bombout(("SSH protocol version 2 required by our configuration " + "but server only provides (old, insecure) SSH-1")); + crStop(0); + } + } else { + /* No longer support values 1 or 2 for CONF_sshprot */ + assert(!"Unexpected value for CONF_sshprot"); } if (s->proto2 && (conf_get_int(ssh->conf, CONF_sshprot) >= 2 || !s->proto1)) @@ -3708,13 +3714,17 @@ static const char *connect_to_host(Ssh ssh, const char *host, int port, } /* - * If the SSH version number's fixed, set it now, and if it's SSH-2, - * send the version string too. + * The SSH version number is always fixed (since we no longer support + * fallback between versions), so set it now, and if it's SSH-2, + * send the version string now too. */ sshprot = conf_get_int(ssh->conf, CONF_sshprot); + assert(sshprot == 0 || sshprot == 3); if (sshprot == 0) + /* SSH-1 only */ ssh->version = 1; if (sshprot == 3 && !ssh->bare_connection) { + /* SSH-2 only */ ssh->version = 2; ssh_send_verstring(ssh, "SSH-", NULL); } -- 2.45.2