struct Packet *pktin);
static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
struct Packet *pktin);
+static void ssh2_channel_check_close(struct ssh_channel *c);
+static void ssh_channel_destroy(struct ssh_channel *c);
/*
* Buffer management constants. There are several of these for
#define OUR_V2_MAXPKT 0x4000UL
#define OUR_V2_PACKETLIMIT 0x9000UL
-/* Maximum length of passwords/passphrases (arbitrary) */
-#define SSH_MAX_PASSWORD_LEN 100
-
const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
const static struct ssh_mac *macs[] = {
CHAN_X11,
CHAN_AGENT,
CHAN_SOCKDATA,
- CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */
+ CHAN_SOCKDATA_DORMANT, /* one the remote hasn't confirmed */
+ /*
+ * CHAN_ZOMBIE is used to indicate a channel for which we've
+ * already destroyed the local data source: for instance, if a
+ * forwarded port experiences a socket error on the local side, we
+ * immediately destroy its local socket and turn the SSH channel
+ * into CHAN_ZOMBIE.
+ */
+ CHAN_ZOMBIE
};
/*
* 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
*
* A channel is completely finished with when all four bits are set.
+ *
+ * In SSH-2, the four bits mean:
+ *
+ * 1 We have sent SSH2_MSG_CHANNEL_EOF.
+ * 2 We have sent SSH2_MSG_CHANNEL_CLOSE.
+ * 4 We have received SSH2_MSG_CHANNEL_EOF.
+ * 8 We have received SSH2_MSG_CHANNEL_CLOSE.
+ *
+ * A channel is completely finished with when we have both sent
+ * and received CLOSE.
+ *
+ * The symbolic constants below use the SSH-2 terminology, which
+ * is a bit confusing in SSH-1, but we have to use _something_.
*/
+#define CLOSES_SENT_EOF 1
+#define CLOSES_SENT_CLOSE 2
+#define CLOSES_RCVD_EOF 4
+#define CLOSES_RCVD_CLOSE 8
int closes;
/*
- * This flag indicates that a close is pending on the outgoing
- * side of the channel: that is, wherever we're getting the data
- * for this channel has sent us some data followed by EOF. We
- * can't actually close the channel until we've finished sending
- * the data, so we set this flag instead to remind us to
- * initiate the closing process once our buffer is clear.
+ * This flag indicates that an EOF is pending on the outgoing side
+ * of the channel: that is, wherever we're getting the data for
+ * this channel has sent us some data followed by EOF. We can't
+ * actually send the EOF until we've finished sending the data, so
+ * we set this flag instead to remind us to do so once our buffer
+ * is clear.
*/
- int pending_close;
+ int pending_eof;
/*
* True if this channel is causing the underlying connection to be
} state;
int size_needed, eof_needed;
+ int sent_console_eof;
+ int got_pty; /* affects EOF behaviour on main channel */
struct Packet **queue;
int queuelen, queuesize;
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("SSH login name");
- /* 512 is an arbitrary upper limit on username size */
- add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512);
+ add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
* Load the public half of any configured keyfile for later use.
*/
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
- if (!filename_is_null(*s->keyfile)) {
+ if (!filename_is_null(s->keyfile)) {
int keytype;
logeventf(ssh, "Reading private key file \"%.150s\"",
filename_to_str(s->keyfile));
s->cur_prompt->name = dupstr("SSH key passphrase");
add_prompt(s->cur_prompt,
dupprintf("Passphrase for key \"%.100s\": ",
- s->publickey_comment),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ s->publickey_comment), FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
(*instr_suf) ? "\n" : "",
instr_suf);
s->cur_prompt->instr_reqd = TRUE;
- add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);
+ add_prompt(s->cur_prompt, prompt, FALSE);
sfree(instr_suf);
}
}
(*instr_suf) ? "\n" : "",
instr_suf);
s->cur_prompt->instr_reqd = TRUE;
- add_prompt(s->cur_prompt, prompt, FALSE, SSH_MAX_PASSWORD_LEN);
+ add_prompt(s->cur_prompt, prompt, FALSE);
sfree(instr_suf);
}
}
s->cur_prompt->name = dupstr("SSH password");
add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
ssh->username, ssh->savedhost),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
}
/*
crFinish(1);
}
-void sshfwd_close(struct ssh_channel *c)
+static void ssh_channel_try_eof(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ assert(c->pending_eof); /* precondition for calling us */
+ if (c->halfopen)
+ return; /* can't close: not even opened yet */
+ if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0)
+ return; /* can't send EOF: pending outgoing data */
+
+ if (ssh->version == 1) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
+ PKT_END);
+ c->closes |= CLOSES_SENT_EOF;
+ } else {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_EOF;
+ if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes)) {
+ /*
+ * Also send MSG_CLOSE.
+ */
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+ }
+ c->pending_eof = FALSE; /* we've sent it now */
+}
+
+void sshfwd_write_eof(struct ssh_channel *c)
{
Ssh ssh = c->ssh;
if (ssh->state == SSH_STATE_CLOSED)
return;
- if (!c->closes) {
- /*
- * If halfopen is true, we have sent
- * CHANNEL_OPEN for this channel, but it hasn't even been
- * acknowledged by the server. So we must set a close flag
- * on it now, and then when the server acks the channel
- * open, we can close it then.
- */
- if (!c->halfopen) {
- if (ssh->version == 1) {
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
- PKT_END);
- c->closes = 1; /* sent MSG_CLOSE */
- } else {
- int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);
- if (bytes_to_send > 0) {
- /*
- * If we still have unsent data in our outgoing
- * buffer for this channel, we can't actually
- * initiate a close operation yet or that data
- * will be lost. Instead, set the pending_close
- * flag so that when we do clear the buffer
- * we'll start closing the channel.
- */
- char logmsg[160] = {'\0'};
- sprintf(
- logmsg,
- "Forwarded port pending to be closed : "
- "%d bytes remaining",
- bytes_to_send);
- logevent(logmsg);
-
- c->pending_close = TRUE;
- } else {
- /*
- * No locally buffered data, so we can send the
- * close message immediately.
- */
- struct Packet *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
- c->closes = 1; /* sent MSG_CLOSE */
- logevent("Nothing left to send, closing channel");
- }
- }
- }
+ if (c->closes & CLOSES_SENT_EOF)
+ return;
- if (c->type == CHAN_X11) {
- c->u.x11.s = NULL;
- logevent("Forwarded X11 connection terminated");
- } else if (c->type == CHAN_SOCKDATA ||
- c->type == CHAN_SOCKDATA_DORMANT) {
- c->u.pfd.s = NULL;
- logevent("Forwarded port closed");
- }
+ c->pending_eof = TRUE;
+ ssh_channel_try_eof(c);
+}
+
+void sshfwd_unclean_close(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ if (ssh->state == SSH_STATE_CLOSED)
+ return;
+
+ if (!(c->closes & CLOSES_SENT_CLOSE)) {
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
+ }
+
+ switch (c->type) {
+ case CHAN_X11:
+ x11_close(c->u.x11.s);
+ break;
+ case CHAN_SOCKDATA:
+ case CHAN_SOCKDATA_DORMANT:
+ pfd_close(c->u.pfd.s);
+ break;
}
+ c->type = CHAN_ZOMBIE;
+
+ ssh2_channel_check_close(c);
}
int sshfwd_write(struct ssh_channel *c, char *buf, int len)
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh->channels, c);
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
+ c->u.a.message = NULL;
add234(ssh->channels, c);
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
PKT_INT, c->remoteid, PKT_INT, c->localid,
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = 0;
c->type = CHAN_SOCKDATA; /* identify channel type */
add234(ssh->channels, c);
pfd_confirm(c->u.pfd.s);
}
- if (c && c->closes) {
+ if (c && c->pending_eof) {
/*
* We have a pending close on this channel,
* which we decided on before the server acked
* the channel open. So now we know the
* remoteid, we can close it again.
*/
- send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE,
- PKT_INT, c->remoteid, PKT_END);
+ ssh_channel_try_eof(c);
}
}
struct ssh_channel *c;
c = find234(ssh->channels, &i, ssh_channelfind);
if (c && !c->halfopen) {
- int closetype;
- closetype =
- (pktin->type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
-
- if ((c->closes == 0) && (c->type == CHAN_X11)) {
- logevent("Forwarded X11 connection terminated");
- assert(c->u.x11.s != NULL);
- x11_close(c->u.x11.s);
- c->u.x11.s = NULL;
- }
- if ((c->closes == 0) && (c->type == CHAN_SOCKDATA)) {
- logevent("Forwarded port closed");
- assert(c->u.pfd.s != NULL);
- pfd_close(c->u.pfd.s);
- c->u.pfd.s = NULL;
- }
- c->closes |= (closetype << 2); /* seen this message */
- if (!(c->closes & closetype)) {
- send_packet(ssh, pktin->type, PKT_INT, c->remoteid,
- PKT_END);
- c->closes |= closetype; /* sent it too */
- }
+ if (pktin->type == SSH1_MSG_CHANNEL_CLOSE &&
+ !(c->closes & CLOSES_RCVD_EOF)) {
+ /*
+ * Received CHANNEL_CLOSE, which we translate into
+ * outgoing EOF.
+ */
+ int send_close = FALSE;
+
+ c->closes |= CLOSES_RCVD_EOF;
+
+ switch (c->type) {
+ case CHAN_X11:
+ if (c->u.x11.s)
+ x11_send_eof(c->u.x11.s);
+ else
+ send_close = TRUE;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s)
+ x11_send_eof(c->u.pfd.s);
+ else
+ send_close = TRUE;
+ case CHAN_AGENT:
+ send_close = TRUE;
+ }
- if (c->closes == 15) {
- del234(ssh->channels, c);
- sfree(c);
- }
+ if (send_close && !(c->closes & CLOSES_SENT_EOF)) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
+ PKT_END);
+ c->closes |= CLOSES_SENT_EOF;
+ }
+ }
+
+ if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION &&
+ !(c->closes & CLOSES_RCVD_CLOSE)) {
+
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d"
+ " for which we never sent CHANNEL_CLOSE\n", i));
+ }
+
+ c->closes |= CLOSES_RCVD_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) &&
+ !(c->closes & CLOSES_SENT_CLOSE)) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_END);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes))
+ ssh_channel_destroy(c);
} else {
bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" :
} else if (pktin->type == SSH1_SMSG_FAILURE) {
c_write_str(ssh, "Server refused to allocate pty\r\n");
ssh->editing = ssh->echoing = 1;
- }
- logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
- ssh->ospeed, ssh->ispeed);
+ } else {
+ logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
+ ssh->ospeed, ssh->ispeed);
+ ssh->got_pty = TRUE;
+ }
} else {
ssh->editing = ssh->echoing = 1;
}
{
Ssh ssh = c->ssh;
struct Packet *pktout;
+ int ret;
while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
int len;
* After having sent as much data as we can, return the amount
* still buffered.
*/
- return bufchain_size(&c->v.v2.outbuffer);
+ ret = bufchain_size(&c->v.v2.outbuffer);
+
+ /*
+ * And if there's no data pending but we need to send an EOF, send
+ * it.
+ */
+ if (!ret && c->pending_eof)
+ ssh_channel_try_eof(c);
+
+ return ret;
}
static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
{
int bufsize;
- if (c->closes)
- return; /* don't send on closing channels */
+ if (c->closes & CLOSES_SENT_EOF)
+ return; /* don't send on channels we've EOFed */
bufsize = ssh2_try_send(c);
if (bufsize == 0) {
switch (c->type) {
break;
}
}
-
- /*
- * If we've emptied the channel's output buffer and there's a
- * pending close event, start the channel-closing procedure.
- */
- if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {
- struct Packet *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
- c->closes = 1;
- c->pending_close = FALSE;
- }
}
/*
Ssh ssh = c->ssh;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
- c->pending_close = FALSE;
+ c->pending_eof = FALSE;
c->throttling_conn = FALSE;
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
Ssh ssh = c->ssh;
/*
- * Never send WINDOW_ADJUST for a channel that the remote side
- * already thinks it's closed; there's no point, since it won't
- * be sending any more data anyway.
+ * Never send WINDOW_ADJUST for a channel that the remote side has
+ * already sent EOF on; there's no point, since it won't be
+ * sending any more data anyway. Ditto if _we've_ already sent
+ * CLOSE.
*/
- if (c->closes != 0)
+ if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
return;
/*
*/
if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
newwin = OUR_V2_MAXPKT;
-
/*
* Only send a WINDOW_ADJUST if there's significantly more window
*/
if (c->v.v2.throttle_state == UNTHROTTLING)
c->v.v2.throttle_state = UNTHROTTLED;
+ /*
+ * We may now initiate channel-closing procedures, if that winadj
+ * was the last thing outstanding before we send CHANNEL_CLOSE.
+ */
+ ssh2_channel_check_close(c);
return TRUE;
}
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- if (!c->closes) {
+ if (!(c->closes & CLOSES_SENT_EOF)) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
ssh2_try_send_and_unthrottle(ssh, c);
}
ssh_agentf_callback, c))
ssh_agentf_callback(c, reply, replylen);
sfree(c->u.a.message);
+ c->u.a.message = NULL;
c->u.a.lensofar = 0;
}
}
}
}
-static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+static void ssh_channel_destroy(struct ssh_channel *c)
{
- struct ssh_channel *c;
+ Ssh ssh = c->ssh;
- c = ssh2_channel_msg(ssh, pktin);
- if (!c)
- return;
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ ssh->mainchan = NULL;
+ update_specials_menu(ssh->frontend);
+ break;
+ case CHAN_X11:
+ if (c->u.x11.s != NULL)
+ x11_close(c->u.x11.s);
+ logevent("Forwarded X11 connection terminated");
+ break;
+ case CHAN_AGENT:
+ sfree(c->u.a.message);
+ break;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s != NULL)
+ pfd_close(c->u.pfd.s);
+ logevent("Forwarded port closed");
+ break;
+ }
+
+ del234(ssh->channels, c);
+ if (ssh->version == 2)
+ bufchain_clear(&c->v.v2.outbuffer);
+ sfree(c);
+
+ /*
+ * See if that was the last channel left open.
+ * (This is only our termination condition if we're
+ * not running in -N mode.)
+ */
+ if (ssh->version == 2 &&
+ !conf_get_int(ssh->conf, CONF_ssh_no_shell) &&
+ count234(ssh->channels) == 0) {
+ /*
+ * We used to send SSH_MSG_DISCONNECT here,
+ * because I'd believed that _every_ conforming
+ * SSH-2 connection had to end with a disconnect
+ * being sent by at least one side; apparently
+ * I was wrong and it's perfectly OK to
+ * unceremoniously slam the connection shut
+ * when you're done, and indeed OpenSSH feels
+ * this is more polite than sending a
+ * DISCONNECT. So now we don't.
+ */
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
+ }
+}
+
+static void ssh2_channel_check_close(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
+ == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.winadj_head) {
+ /*
+ * We have both sent and received EOF, and we have no
+ * outstanding winadj channel requests, which means the
+ * channel is in final wind-up. But we haven't sent CLOSE, so
+ * let's do so now.
+ */
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
+ /*
+ * We have both sent and received CLOSE, which means we're
+ * completely done with the channel.
+ */
+ ssh_channel_destroy(c);
+ }
+}
+
+static void ssh2_channel_got_eof(struct ssh_channel *c)
+{
+ if (c->closes & CLOSES_RCVD_EOF)
+ return; /* already seen EOF */
+ c->closes |= CLOSES_RCVD_EOF;
if (c->type == CHAN_X11) {
- /*
- * Remote EOF on an X11 channel means we should
- * wrap up and close the channel ourselves.
- */
- x11_close(c->u.x11.s);
- c->u.x11.s = NULL;
- sshfwd_close(c);
+ x11_send_eof(c->u.x11.s);
} else if (c->type == CHAN_AGENT) {
- sshfwd_close(c);
+ /* Manufacture an outgoing EOF in response to the incoming one. */
+ sshfwd_write_eof(c);
} else if (c->type == CHAN_SOCKDATA) {
- pfd_close(c->u.pfd.s);
- c->u.pfd.s = NULL;
- sshfwd_close(c);
+ pfd_send_eof(c->u.pfd.s);
+ } else if (c->type == CHAN_MAINSESSION) {
+ Ssh ssh = c->ssh;
+
+ if (!ssh->sent_console_eof &&
+ (from_backend_eof(ssh->frontend) || ssh->got_pty)) {
+ /*
+ * Either from_backend_eof told us that the front end
+ * wants us to close the outgoing side of the connection
+ * as soon as we see EOF from the far end, or else we've
+ * unilaterally decided to do that because we've allocated
+ * a remote pty and hence EOF isn't a particularly
+ * meaningful concept.
+ */
+ sshfwd_write_eof(c);
+ }
+ ssh->sent_console_eof = TRUE;
}
+
+ ssh2_channel_check_close(c);
+}
+
+static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ ssh2_channel_got_eof(c);
}
static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- struct Packet *pktout;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
return;
- /* Do pre-close processing on the channel. */
- switch (c->type) {
- case CHAN_MAINSESSION:
- ssh->mainchan = NULL;
- update_specials_menu(ssh->frontend);
- break;
- case CHAN_X11:
- if (c->u.x11.s != NULL)
- x11_close(c->u.x11.s);
- sshfwd_close(c);
- break;
- case CHAN_AGENT:
- sshfwd_close(c);
- break;
- case CHAN_SOCKDATA:
- if (c->u.pfd.s != NULL)
- pfd_close(c->u.pfd.s);
- sshfwd_close(c);
- break;
- }
- if (c->closes == 0) {
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
+
+ /*
+ * When we receive CLOSE on a channel, we assume it comes with an
+ * implied EOF if we haven't seen EOF yet.
+ */
+ ssh2_channel_got_eof(c);
+
+ /*
+ * And we also send an outgoing EOF, if we haven't already, on the
+ * assumption that CLOSE is a pretty forceful announcement that
+ * the remote side is doing away with the entire channel. (If it
+ * had wanted to send us EOF and continue receiving data from us,
+ * it would have just sent CHANNEL_EOF.)
+ */
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ /*
+ * Make sure we don't read any more from whatever our local
+ * data source is for this channel.
+ */
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ ssh->send_ok = 0; /* stop trying to read from stdin */
+ break;
+ case CHAN_X11:
+ x11_override_throttle(c->u.x11.s, 1);
+ break;
+ case CHAN_SOCKDATA:
+ pfd_override_throttle(c->u.pfd.s, 1);
+ break;
+ }
+
+ /*
+ * Send outgoing EOF.
+ */
+ sshfwd_write_eof(c);
}
- del234(ssh->channels, c);
- bufchain_clear(&c->v.v2.outbuffer);
- sfree(c);
/*
- * See if that was the last channel left open.
- * (This is only our termination condition if we're
- * not running in -N mode.)
+ * Now process the actual close.
*/
- if (!conf_get_int(ssh->conf, CONF_ssh_no_shell) && count234(ssh->channels) == 0) {
- /*
- * We used to send SSH_MSG_DISCONNECT here,
- * because I'd believed that _every_ conforming
- * SSH-2 connection had to end with a disconnect
- * being sent by at least one side; apparently
- * I was wrong and it's perfectly OK to
- * unceremoniously slam the connection shut
- * when you're done, and indeed OpenSSH feels
- * this is more polite than sending a
- * DISCONNECT. So now we don't.
- */
- ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
+ if (!(c->closes & CLOSES_RCVD_CLOSE)) {
+ c->closes |= CLOSES_RCVD_CLOSE;
+ ssh2_channel_check_close(c);
}
}
static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
{
struct ssh_channel *c;
- struct Packet *pktout;
c = ssh2_channel_msg(ssh, pktin);
if (!c)
c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
if (c->u.pfd.s)
pfd_confirm(c->u.pfd.s);
- if (c->closes) {
- /*
- * We have a pending close on this channel,
- * which we decided on before the server acked
- * the channel open. So now we know the
- * remoteid, we can close it again.
- */
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
- }
+ if (c->pending_eof)
+ ssh_channel_try_eof(c);
}
static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
AUTH_TYPE_PASSWORD,
- AUTH_TYPE_GSSAPI,
+ AUTH_TYPE_GSSAPI, /* always QUIET */
AUTH_TYPE_KEYBOARD_INTERACTIVE,
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
} type;
* for later use.
*/
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
- if (!filename_is_null(*s->keyfile)) {
+ if (!filename_is_null(s->keyfile)) {
int keytype;
logeventf(ssh, "Reading private key file \"%.150s\"",
filename_to_str(s->keyfile));
s->cur_prompt = new_prompts(ssh->frontend);
s->cur_prompt->to_server = TRUE;
s->cur_prompt->name = dupstr("SSH login name");
- /* 512 is an arbitrary limit :-( */
- add_prompt(s->cur_prompt, dupstr("login as: "), TRUE, 512);
+ add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
/*
* We have received an unequivocal Access
* Denied. This can translate to a variety of
- * messages:
- *
- * - if we'd just tried "none" authentication,
- * it's not worth printing anything at all
- *
- * - if we'd just tried a public key _offer_,
- * the message should be "Server refused our
- * key" (or no message at all if the key
- * came from Pageant)
- *
- * - if we'd just tried anything else, the
- * message really should be "Access denied".
- *
+ * messages, or no message at all.
+ *
+ * For forms of authentication which are attempted
+ * implicitly, by which I mean without printing
+ * anything in the window indicating that we're
+ * trying them, we should never print 'Access
+ * denied'.
+ *
+ * If we do print a message saying that we're
+ * attempting some kind of authentication, it's OK
+ * to print a followup message saying it failed -
+ * but the message may sometimes be more specific
+ * than simply 'Access denied'.
+ *
* Additionally, if we'd just tried password
* authentication, we should break out of this
* whole loop so as to go back to the username
s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
c_write_str(ssh, "Server refused our key\r\n");
- logevent("Server refused public key");
+ logevent("Server refused our key");
+ } else if (s->type == AUTH_TYPE_PUBLICKEY) {
+ /* This _shouldn't_ happen except by a
+ * protocol bug causing client and server to
+ * disagree on what is a correct signature. */
+ c_write_str(ssh, "Server refused public-key signature"
+ " despite accepting key!\r\n");
+ logevent("Server refused public-key signature"
+ " despite accepting key!");
} else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
- /* server declined keyboard-interactive; ignore */
- } else {
+ /* quiet, so no c_write */
+ logevent("Server refused keyboard-interactive authentication");
+ } else if (s->type==AUTH_TYPE_GSSAPI) {
+ /* always quiet, so no c_write */
+ /* also, the code down in the GSSAPI block has
+ * already logged this in the Event Log */
+ } else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) {
+ logevent("Keyboard-interactive authentication failed");
+ c_write_str(ssh, "Access denied\r\n");
+ } else {
+ assert(s->type == AUTH_TYPE_PASSWORD);
+ logevent("Password authentication failed");
c_write_str(ssh, "Access denied\r\n");
- logevent("Access denied");
- if (s->type == AUTH_TYPE_PASSWORD &&
- conf_get_int(ssh->conf, CONF_change_username)) {
+
+ if (conf_get_int(ssh->conf, CONF_change_username)) {
/* XXX perhaps we should allow
* keyboard-interactive to do this too? */
s->we_are_in = FALSE;
add_prompt(s->cur_prompt,
dupprintf("Passphrase for key \"%.100s\": ",
s->publickey_comment),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
ssh->send_ok = 1;
sfree(sigdata);
ssh2_pkt_send(ssh, s->pktout);
+ logevent("Sent public key signature");
s->type = AUTH_TYPE_PUBLICKEY;
key->alg->freekey(key->data);
}
ssh2_pkt_addstring(s->pktout, ssh->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
+ logevent("Attempting GSSAPI authentication");
/* add mechanism info */
s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
ssh2_pkt_addstring(s->pktout, ""); /* lang */
ssh2_pkt_addstring(s->pktout, ""); /* submethods */
ssh2_pkt_send(ssh, s->pktout);
+
+ logevent("Attempting keyboard-interactive authentication");
crWaitUntilV(pktin);
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
* user without actually issuing any prompts).
* Give up on it entirely. */
s->gotit = TRUE;
- if (pktin->type == SSH2_MSG_USERAUTH_FAILURE)
- logevent("Keyboard-interactive authentication refused");
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
s->kbd_inter_refused = TRUE; /* don't try it again */
continue;
}
add_prompt(s->cur_prompt,
dupprintf("%.*s", prompt_len, prompt),
- echo, SSH_MAX_PASSWORD_LEN);
+ echo);
}
if (name_len) {
}
ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
+ /*
+ * Free the prompts structure from this iteration.
+ * If there's another, a new one will be allocated
+ * when we return to the top of this while loop.
+ */
+ free_prompts(s->cur_prompt);
+
/*
* Get the next packet in case it's another
* INFO_REQUEST.
add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
ssh->username,
ssh->savedhost),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
ret = get_userpass_input(s->cur_prompt, NULL, 0);
while (ret < 0) {
*/
add_prompt(s->cur_prompt,
dupstr("Current password (blank for previously entered password): "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
add_prompt(s->cur_prompt, dupstr("Enter new password: "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
- FALSE, SSH_MAX_PASSWORD_LEN);
+ FALSE);
/*
* Loop until the user manages to enter the same
} else {
logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
ssh->ospeed, ssh->ispeed);
+ ssh->got_pty = TRUE;
}
} else {
ssh->editing = ssh->echoing = 1;
bufchain_init(&ssh->queued_incoming_data);
ssh->frozen = FALSE;
ssh->username = NULL;
+ ssh->sent_console_eof = FALSE;
+ ssh->got_pty = FALSE;
*backend_handle = ssh;
if (ssh->version == 1) {
return override_value;
} else if (ssh->version == 2) {
- if (!ssh->mainchan || ssh->mainchan->closes > 0)
+ if (!ssh->mainchan)
return override_value;
else
return (override_value +
if (ssh->version == 1) {
send_packet(ssh, SSH1_CMSG_EOF, PKT_END);
} else if (ssh->mainchan) {
- struct Packet *pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
- ssh2_pkt_adduint32(pktout, ssh->mainchan->remoteid);
- ssh2_pkt_send(ssh, pktout);
+ sshfwd_write_eof(ssh->mainchan);
ssh->send_ok = 0; /* now stop trying to read from stdin */
}
logevent("Sent EOF message");
}
}
}
+
+ /*
+ * Now process any SSH connection data that was stashed in our
+ * queue while we were frozen.
+ */
+ ssh_process_queued_incoming_data(ssh);
}
void ssh_send_port_open(void *channel, char *hostname, int port, char *org)