ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
HELPCTX(ssh_bugs_maxpkt2),
sshbug_handler, I(CONF_sshbug_maxpkt2));
+ ctrl_droplist(s, "Replies to channel requests after channel close",
+ 'q', 20, HELPCTX(ssh_bugs_chanreq),
+ sshbug_handler, I(CONF_sshbug_chanreq));
}
}
}
\cq{winadj@putty.projects.tartarus.org} request, and will make do
without its timing data.
+\S{config-ssh-bug-chanreq} \q{Replies to channel requests after channel close}
+
+\cfg{winhelp-topic}{ssh.bugs.chanreq}
+
+The SSH protocol as published in RFC 4254 has an ambiguity which
+arises if one side of a connection tries to close a channel, while the
+other side simultaneously sends a request within the channel and asks
+for a reply. RFC 4254 leaves it unclear whether the closing side
+should reply to the channel request after having announced its
+intention to close the channel.
+
+Discussion on the \cw{ietf-ssh} mailing list in April 2014 formed a
+clear consensus that the right answer is no. However, because of the
+ambiguity in the specification, some SSH servers have implemented the
+other policy; for example,
+\W{https://bugzilla.mindrot.org/show_bug.cgi?id=1818}{OpenSSH used to}
+until it was fixed.
+
+Because PuTTY sends channel requests with the \q{want reply} flag
+throughout channels' lifetime (see \k{config-ssh-bug-winadj}), it's
+possible that when connecting to such a server it might receive a
+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}.
+
\H{config-serial} The Serial panel
The \i{Serial} panel allows you to configure options that only apply
X(INT, NONE, sshbug_maxpkt2) \
X(INT, NONE, sshbug_ignore2) \
X(INT, NONE, sshbug_winadj) \
+ X(INT, NONE, sshbug_chanreq) \
/* \
* ssh_simple means that we promise never to open any channel \
* other than the main one, which means it can safely use a very \
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, "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));
write_setting_i(sesskey, "LoginShell", conf_get_int(conf, CONF_login_shell));
write_setting_i(sesskey, "ScrollbarOnLeft", conf_get_int(conf, CONF_scrollbar_on_left));
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, "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);
gppi(sesskey, "StampUtmp", 1, conf, CONF_stamp_utmp);
gppi(sesskey, "LoginShell", 1, conf, CONF_login_shell);
#define BUG_SSH2_MAXPKT 256
#define BUG_CHOKES_ON_SSH2_IGNORE 512
#define BUG_CHOKES_ON_WINADJ 1024
+#define BUG_SENDS_LATE_REQUEST_REPLY 2048
/*
* Codes for terminal modes.
ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ;
logevent("We believe remote version has winadj bug");
}
+
+ if (conf_get_int(ssh->conf, CONF_sshbug_chanreq) == FORCE_ON ||
+ (conf_get_int(ssh->conf, CONF_sshbug_chanreq) == AUTO &&
+ (wc_match("OpenSSH_[2-5].*", imp) ||
+ wc_match("OpenSSH_6.[0-6]*", imp)))) {
+ /*
+ * These versions have the SSH-2 channel request bug. 6.7 and
+ * above do not:
+ * https://bugzilla.mindrot.org/show_bug.cgi?id=1818
+ */
+ ssh->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY;
+ logevent("We believe remote version has SSH-2 channel request bug");
+ }
}
/*
* request-specific data added and be sent. Note that if a handler is
* provided, it's essential that the request actually be sent.
*
- * The handler will usually be passed the response packet in pktin.
- * If pktin is NULL, this means that no reply will ever be forthcoming
- * (e.g. because the entire connection is being destroyed) and the
- * handler should free any storage it's holding.
+ * The handler will usually be passed the response packet in pktin. If
+ * pktin is NULL, this means that no reply will ever be forthcoming
+ * (e.g. because the entire connection is being destroyed, or because
+ * the server initiated channel closure before we saw the response)
+ * and the handler should free any storage it's holding.
*/
static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type,
cchandler_fn_t handler, void *ctx)
*/
ssh2_channel_got_eof(c);
+ if (!(ssh->remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) {
+ /*
+ * It also means we stop expecting to see replies to any
+ * outstanding channel requests, so clean those up too.
+ * (ssh_chanreq_init will enforce by assertion that we don't
+ * subsequently put anything back on this list.)
+ */
+ while (c->v.v2.chanreq_head) {
+ struct outstanding_channel_request *ocr = c->v.v2.chanreq_head;
+ ocr->handler(c, NULL, ocr->ctx);
+ c->v.v2.chanreq_head = ocr->next;
+ sfree(ocr);
+ }
+ }
+
/*
* And we also send an outgoing EOF, if we haven't already, on the
* assumption that CLOSE is a pretty forceful announcement that
ssh_pkt_getstring(pktin, &type, &typelen);
want_reply = ssh2_pkt_getbool(pktin);
+ if (c->closes & CLOSES_SENT_CLOSE) {
+ /*
+ * We don't reply to channel requests after we've sent
+ * CHANNEL_CLOSE for the channel, because our reply might
+ * cross in the network with the other side's CHANNEL_CLOSE
+ * and arrive after they have wound the channel up completely.
+ */
+ want_reply = FALSE;
+ }
+
/*
* Having got the channel number, we now look at
* the request type string to see if it's something
static void ssh2_response_authconn(struct ssh_channel *c, struct Packet *pktin,
void *ctx)
{
- do_ssh2_authconn(c->ssh, NULL, 0, pktin);
+ if (pktin)
+ do_ssh2_authconn(c->ssh, NULL, 0, pktin);
}
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
#define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2:config-ssh-bug-rekey"
#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_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"