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);
/*
#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
};
/*
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);
}
/*
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)
{
Ssh ssh = c->ssh;
} 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;
}
/*
* 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.
+ * sending any more data anyway. Ditto if _we've_ already sent
+ * CLOSE.
*/
- if (c->closes & CLOSES_RCVD_EOF)
+ 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;
}
struct Packet *pktout;
if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
- == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF)) {
+ == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.winadj_head) {
/*
- * We have both sent and received EOF, which means the channel
- * is in final wind-up. But we haven't sent CLOSE, so let's.
+ * 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);
} else if (c->type == CHAN_MAINSESSION) {
Ssh ssh = c->ssh;
- if (!ssh->sent_console_eof && from_backend_eof(ssh->frontend)) {
+ if (!ssh->sent_console_eof &&
+ (from_backend_eof(ssh->frontend) || ssh->got_pty)) {
/*
- * The front end wants us to close the outgoing side of the
- * connection as soon as we see EOF from the far end.
+ * 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);
}
*/
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);
+ }
+
/*
* Now process the actual close.
*/
* 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;
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;
}
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;
ssh->frozen = FALSE;
ssh->username = NULL;
ssh->sent_console_eof = FALSE;
+ ssh->got_pty = FALSE;
*backend_handle = ssh;