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));
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
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) \
/* \
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));
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);
#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.
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);
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
* 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);
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);
}
#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 */
#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"