X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/bc06669bcf2c0d331e231d18c0e14023433f9e79..fc40b431a9049585675e171643eb308cc548713c:/ssh.c diff --git a/ssh.c b/ssh.c index 291d58c4..ef503bf5 100644 --- a/ssh.c +++ b/ssh.c @@ -473,6 +473,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, 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); /* @@ -516,9 +517,6 @@ 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[] = { @@ -560,7 +558,15 @@ enum { /* channel types */ 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 }; /* @@ -849,6 +855,7 @@ struct ssh_tag { int size_needed, eof_needed; int sent_console_eof; + int got_pty; /* affects EOF behaviour on main channel */ struct Packet **queue; int queuelen, queuesize; @@ -3524,8 +3531,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, 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; @@ -3571,7 +3577,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, * 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)); @@ -3818,8 +3824,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, 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; @@ -3974,7 +3979,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*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); } } @@ -4017,7 +4022,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, (*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); } } @@ -4030,7 +4035,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, 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); } /* @@ -4253,6 +4258,35 @@ void sshfwd_write_eof(struct ssh_channel *c) 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; @@ -5180,9 +5214,11 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen, } 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; } @@ -5742,6 +5778,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } } + if (!ssh->hostkey) { + bombout(("Couldn't agree a host key algorithm (available: %s)", + str ? str : "(null)")); + crStop(0); + } + s->guessok = s->guessok && first_in_commasep_string(hostkey_algs[0]->name, str, len); ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ @@ -6552,9 +6594,10 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) /* * 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; /* @@ -6564,7 +6607,6 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ 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 @@ -6667,6 +6709,11 @@ static int ssh2_handle_winadj_response(struct ssh_channel *c) */ 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; } @@ -6888,10 +6935,12 @@ static void ssh2_channel_check_close(struct ssh_channel *c) 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); @@ -6924,10 +6973,15 @@ static void ssh2_channel_got_eof(struct ssh_channel *c) } 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); } @@ -6962,6 +7016,36 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) 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. */ if (!(c->closes & CLOSES_RCVD_CLOSE)) { @@ -7483,7 +7567,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * 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)); @@ -7629,8 +7713,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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; @@ -8036,7 +8119,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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; @@ -8416,7 +8499,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } add_prompt(s->cur_prompt, dupprintf("%.*s", prompt_len, prompt), - echo, SSH_MAX_PASSWORD_LEN); + echo); } if (name_len) { @@ -8484,6 +8567,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } 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. @@ -8513,7 +8603,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, 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) { @@ -8615,11 +8705,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ 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 @@ -9010,6 +9100,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } else { logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; } } else { ssh->editing = ssh->echoing = 1; @@ -9437,6 +9528,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->frozen = FALSE; ssh->username = NULL; ssh->sent_console_eof = FALSE; + ssh->got_pty = FALSE; *backend_handle = ssh; @@ -9951,6 +10043,12 @@ static void ssh_unthrottle(void *handle, int bufsize) } } } + + /* + * 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)