X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/997097db8b9bd66341290cda4effcd93fa74e4d0..2e0aae4f59a1481b82dd5ce16f579d4ec9b2c09b:/ssh.c diff --git a/ssh.c b/ssh.c index 0f30fe01..351003fd 100644 --- a/ssh.c +++ b/ssh.c @@ -196,6 +196,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_SSH2_PK_SESSIONID 128 #define BUG_SSH2_MAXPKT 256 #define BUG_CHOKES_ON_SSH2_IGNORE 512 +#define BUG_CHOKES_ON_WINADJ 1024 /* * Codes for terminal modes. @@ -517,9 +518,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[] = { @@ -561,7 +559,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 }; /* @@ -850,6 +856,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; @@ -2574,6 +2581,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; logevent("We believe remote version has SSH-2 ignore bug"); } + + if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) { + /* + * Servers that don't support our winadj request for one + * reason or another. Currently, none detected automatically. + */ + ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ; + logevent("We believe remote version has winadj bug"); + } } /* @@ -3525,8 +3541,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; @@ -3572,7 +3587,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)); @@ -3819,8 +3834,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; @@ -3845,7 +3859,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ret = loadrsakey(s->keyfile, &s->key, passphrase, &error); if (passphrase) { - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (ret == 1) { @@ -3975,7 +3989,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); } } @@ -4018,7 +4032,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); } } @@ -4031,7 +4045,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); } /* @@ -4254,6 +4268,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; @@ -5181,9 +5224,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; } @@ -5743,6 +5788,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 */ @@ -6253,7 +6304,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->csmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s client->server encryption", @@ -6319,7 +6370,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, assert(ssh->scmac->len <= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); - memset(keyspace, 0, sizeof(keyspace)); + smemclr(keyspace, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s server->client encryption", ssh->sccipher->text_name); @@ -6553,9 +6604,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; /* @@ -6565,7 +6617,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 @@ -6594,7 +6645,8 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) * unexpected CHANNEL_FAILUREs. */ if (newwin == c->v.v2.locmaxwin && - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) { + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] && + !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) { pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org"); @@ -6932,10 +6984,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); } @@ -6970,6 +7027,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)) { @@ -7491,7 +7578,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)); @@ -7637,8 +7724,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; @@ -8044,7 +8130,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; @@ -8076,7 +8162,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, key = ssh2_load_userkey(s->keyfile, passphrase, &error); if (passphrase) { /* burn the evidence */ - memset(passphrase, 0, strlen(passphrase)); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (key == SSH2_WRONG_PASSPHRASE || key == NULL) { @@ -8424,7 +8510,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) { @@ -8492,6 +8578,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. @@ -8521,7 +8614,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) { @@ -8623,11 +8716,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 @@ -8648,7 +8741,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ /* burn the evidence */ free_prompts(s->cur_prompt); - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); ssh_disconnect(ssh, NULL, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER, @@ -8664,7 +8757,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * re-enter it if they louse up the new password.) */ if (s->cur_prompt->prompts[0]->result[0]) { - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); s->password = @@ -8731,7 +8824,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * We don't need the old password any more, in any * case. Burn the evidence. */ - memset(s->password, 0, strlen(s->password)); + smemclr(s->password, strlen(s->password)); sfree(s->password); } else { @@ -9018,6 +9111,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; @@ -9445,6 +9539,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; @@ -9959,6 +10054,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)