X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/a699eb41b9aa738514f36a8e5f3007495a52b05b..5ab93329d5698739fcf00d3d52d69c8936ecd3f4:/ssh.c diff --git a/ssh.c b/ssh.c index 7deb90c0..e2105572 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. @@ -430,10 +431,12 @@ enum { * Database for Edit and Continue'. */ #define crBegin(v) { int *crLine = &v; switch(v) { case 0:; -#define crState(t) \ - struct t *s; \ - if (!ssh->t) ssh->t = snew(struct t); \ - s = ssh->t; +#define crBeginState crBegin(s->crLine) +#define crStateP(t, v) \ + struct t *s; \ + if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \ + s = (v); +#define crState(t) crStateP(t, ssh->t) #define crFinish(z) } *crLine = 0; return (z); } #define crFinishV } *crLine = 0; return; } #define crReturn(z) \ @@ -517,9 +520,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 +561,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 }; /* @@ -882,12 +890,8 @@ struct ssh_tag { int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; - int do_ssh_init_crstate; int ssh_gotdata_crstate; - int do_ssh1_login_crstate; int do_ssh1_connection_crstate; - int do_ssh2_transport_crstate; - int do_ssh2_authconn_crstate; void *do_ssh_init_state; void *do_ssh1_login_state; @@ -956,6 +960,7 @@ struct ssh_tag { * indications from a request. */ struct queued_handler *qhead, *qtail; + handler_fn_t q_saved_handler1, q_saved_handler2; /* * This module deals with sending keepalives. @@ -2575,6 +2580,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"); + } } /* @@ -2644,6 +2658,7 @@ static void ssh_send_verstring(Ssh ssh, char *svers) static int do_ssh_init(Ssh ssh, unsigned char c) { struct do_ssh_init_state { + int crLine; int vslen; char version[10]; char *vstring; @@ -2652,8 +2667,8 @@ static int do_ssh_init(Ssh ssh, unsigned char c) int proto1, proto2; }; crState(do_ssh_init_state); - - crBegin(ssh->do_ssh_init_crstate); + + crBeginState; /* Search for a line beginning with the string "SSH-" in the input. */ for (;;) { @@ -3242,6 +3257,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, struct RSAKey servkey, hostkey; struct MD5Context md5c; struct do_ssh1_login_state { + int crLine; int len; unsigned char *rsabuf, *keystr1, *keystr2; unsigned long supported_ciphers_mask, supported_auths_mask; @@ -3269,7 +3285,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, }; crState(do_ssh1_login_state); - crBegin(ssh->do_ssh1_login_crstate); + crBeginState; if (!pktin) crWaitUntil(pktin); @@ -3526,8 +3542,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; @@ -3573,7 +3588,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)); @@ -3820,8 +3835,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; @@ -3846,7 +3860,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) { @@ -3976,7 +3990,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); } } @@ -4019,7 +4033,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); } } @@ -4032,7 +4046,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); } /* @@ -4255,6 +4269,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; @@ -4311,27 +4354,26 @@ static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin) if (qh->msg1 > 0) { assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg1] = NULL; + ssh->packet_dispatch[qh->msg1] = ssh->q_saved_handler1; } if (qh->msg2 > 0) { assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler); - ssh->packet_dispatch[qh->msg2] = NULL; + ssh->packet_dispatch[qh->msg2] = ssh->q_saved_handler2; } if (qh->next) { ssh->qhead = qh->next; if (ssh->qhead->msg1 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler; } if (ssh->qhead->msg2 > 0) { - assert(ssh->packet_dispatch[ssh->qhead->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler; } } else { ssh->qhead = ssh->qtail = NULL; - ssh->packet_dispatch[pktin->type] = NULL; } qh->handler(ssh, pktin, qh->ctx); @@ -4355,11 +4397,11 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2, ssh->qhead = qh; if (qh->msg1 > 0) { - assert(ssh->packet_dispatch[qh->msg1] == NULL); + ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1]; ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler; } if (qh->msg2 > 0) { - assert(ssh->packet_dispatch[qh->msg2] == NULL); + ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2]; ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler; } } else { @@ -4917,13 +4959,16 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin) x11_send_eof(c->u.x11.s); else send_close = TRUE; + break; case CHAN_SOCKDATA: if (c->u.pfd.s) - x11_send_eof(c->u.pfd.s); + pfd_send_eof(c->u.pfd.s); else send_close = TRUE; + break; case CHAN_AGENT: send_close = TRUE; + break; } if (send_close && !(c->closes & CLOSES_SENT_EOF)) { @@ -5433,6 +5478,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, { unsigned char *in = (unsigned char *)vin; struct do_ssh2_transport_state { + int crLine; int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher; Bignum p, g, e, f, K; void *our_kexinit; @@ -5466,7 +5512,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, }; crState(do_ssh2_transport_state); - crBegin(ssh->do_ssh2_transport_crstate); + crBeginState; s->cscipher_tobe = s->sccipher_tobe = NULL; s->csmac_tobe = s->scmac_tobe = NULL; @@ -5487,7 +5533,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, begin_key_exchange: ssh->pkt_kctx = SSH2_PKTCTX_NOKEX; { - int i, j, commalist_started; + int i, j, k, commalist_started; /* * Set up the preferred key exchange. (NULL => warn below here) @@ -5599,46 +5645,30 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, if (i < lenof(hostkey_algs) - 1) ssh2_pkt_addstring_str(s->pktout, ","); } - /* List client->server encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) - ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; + /* List encryption algorithms (client->server then server->client). */ + for (k = 0; k < 2; k++) { + ssh2_pkt_addstring_start(s->pktout); + commalist_started = 0; + for (i = 0; i < s->n_preferred_ciphers; i++) { + const struct ssh2_ciphers *c = s->preferred_ciphers[i]; + if (!c) continue; /* warning flag */ + for (j = 0; j < c->nciphers; j++) { + if (commalist_started) + ssh2_pkt_addstring_str(s->pktout, ","); + ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); + commalist_started = 1; + } } } - /* List server->client encryption algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - commalist_started = 0; - for (i = 0; i < s->n_preferred_ciphers; i++) { - const struct ssh2_ciphers *c = s->preferred_ciphers[i]; - if (!c) continue; /* warning flag */ - for (j = 0; j < c->nciphers; j++) { - if (commalist_started) + /* List MAC algorithms (client->server then server->client). */ + for (j = 0; j < 2; j++) { + ssh2_pkt_addstring_start(s->pktout); + for (i = 0; i < s->nmacs; i++) { + ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); + if (i < s->nmacs - 1) ssh2_pkt_addstring_str(s->pktout, ","); - ssh2_pkt_addstring_str(s->pktout, c->list[j]->name); - commalist_started = 1; } } - /* List client->server MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } - /* List server->client MAC algorithms. */ - ssh2_pkt_addstring_start(s->pktout); - for (i = 0; i < s->nmacs; i++) { - ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name); - if (i < s->nmacs - 1) - ssh2_pkt_addstring_str(s->pktout, ","); - } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the * same set twice.) */ @@ -5746,6 +5776,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 */ @@ -6256,7 +6292,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", @@ -6322,7 +6358,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); @@ -6358,19 +6394,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh2_timer, ssh); /* - * If this is the first key exchange phase, we must pass the - * SSH2_MSG_NEWKEYS packet to the next layer, not because it - * wants to see it but because it will need time to initialise - * itself before it sees an actual packet. In subsequent key - * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because - * it would only confuse the layer above. - */ - if (s->activated_authconn) { - crReturn(0); - } - s->activated_authconn = TRUE; - - /* * Now we're encrypting. Begin returning 1 to the protocol main * function so that other things can run on top of the * transport. If we ever see a KEXINIT, we must go back to the @@ -6388,6 +6411,13 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) || (!pktin && inlen < 0))) { wait_for_rekey: + if (!ssh->protocol_initial_phase_done) { + ssh->protocol_initial_phase_done = TRUE; + /* + * Allow authconn to initialise itself. + */ + do_ssh2_authconn(ssh, NULL, 0, NULL); + } crReturn(1); } if (pktin) { @@ -6556,9 +6586,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; /* @@ -6568,7 +6599,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 @@ -6597,7 +6627,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"); @@ -7004,7 +7035,7 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin) /* * Send outgoing EOF. */ - sshfwd_write_eof(ssh->mainchan); + sshfwd_write_eof(c); } /* @@ -7421,10 +7452,16 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val) /* * Handle the SSH-2 userauth and connection layers. */ +static void ssh2_msg_authconn(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_authconn(ssh, NULL, 0, pktin); +} + static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, struct Packet *pktin) { struct do_ssh2_authconn_state { + int crLine; enum { AUTH_TYPE_NONE, AUTH_TYPE_PUBLICKEY, @@ -7463,6 +7500,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int siglen, retlen, len; char *q, *agentreq, *ret; int try_send; + int requested_x11; + int requested_agent; + int requested_tty; int num_env, env_left, env_ok; struct Packet *pktout; Filename *keyfile; @@ -7477,8 +7517,33 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, }; crState(do_ssh2_authconn_state); - crBegin(ssh->do_ssh2_authconn_crstate); - + crBeginState; + + /* Register as a handler for all the messages this coroutine handles. */ + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_authconn; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_authconn; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_authconn; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_authconn; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_authconn; + s->done_service_req = FALSE; s->we_are_in = s->userauth_success = FALSE; #ifndef NO_GSSAPI @@ -7529,7 +7594,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)); @@ -7675,8 +7740,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; @@ -8082,7 +8146,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; @@ -8114,7 +8178,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) { @@ -8462,7 +8526,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) { @@ -8530,6 +8594,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. @@ -8559,7 +8630,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) { @@ -8661,11 +8732,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 @@ -8686,7 +8757,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, @@ -8702,7 +8773,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 = @@ -8769,7 +8840,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 { @@ -8944,6 +9015,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* + * Enable port forwardings. + */ + ssh_setup_portfwd(ssh, ssh->conf); + + /* + * Send the CHANNEL_REQUESTS for the main channel. We send them all + * and then start looking for responses, so it's important that the + * sending and receiving code below it is kept in sync. + */ + + /* * Potentially enable X11 forwarding. */ if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) && @@ -8968,26 +9050,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, end_log_omission(ssh, s->pktout); ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum); ssh2_pkt_send(ssh, s->pktout); - - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to X11 forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("X11 forwarding refused"); - } else { - logevent("X11 forwarding enabled"); - ssh->X11_fwd_enabled = TRUE; - } - } - - /* - * Enable port forwardings. - */ - ssh_setup_portfwd(ssh, ssh->conf); + s->requested_x11 = TRUE; + } else + s->requested_x11 = FALSE; /* * Potentially enable agent forwarding. @@ -8999,21 +9064,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com"); ssh2_pkt_addbool(s->pktout, 1); /* want reply */ ssh2_pkt_send(ssh, s->pktout); - - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to agent forwarding request:" - " packet type %d", pktin->type)); - crStopV; - } - logevent("Agent forwarding refused"); - } else { - logevent("Agent forwarding enabled"); - ssh->agentfwd_enabled = TRUE; - } - } + s->requested_agent = TRUE; + } else + s->requested_agent = FALSE; /* * Now allocate a pty for the session. @@ -9042,25 +9095,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */ ssh2_pkt_send(ssh, s->pktout); ssh->state = SSH_STATE_INTERMED; - - crWaitUntilV(pktin); - - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to pty request:" - " packet type %d", pktin->type)); - crStopV; - } - c_write_str(ssh, "Server refused to allocate pty\r\n"); - ssh->editing = ssh->echoing = 1; - } else { - logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", - ssh->ospeed, ssh->ispeed); - ssh->got_pty = TRUE; - } - } else { - ssh->editing = ssh->echoing = 1; - } + s->requested_tty = TRUE; + } else + s->requested_tty = FALSE; /* * Send environment variables. @@ -9068,11 +9105,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Simplest thing here is to send all the requests at once, and * then wait for a whole bunch of successes or failures. */ + s->num_env = 0; if (ssh->mainchan && !ssh->ncmode) { char *key, *val; - s->num_env = 0; - for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) { @@ -9086,39 +9122,96 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->num_env++; } - - if (s->num_env) { + if (s->num_env) logeventf(ssh, "Sent %d environment variables", s->num_env); + } - s->env_ok = 0; - s->env_left = s->num_env; + /* + * All CHANNEL_REQUESTs sent. Now collect up the replies. These + * must be in precisely the same order as the requests. + */ - while (s->env_left > 0) { - crWaitUntilV(pktin); + if (s->requested_x11) { + crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { - if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Unexpected response to environment request:" - " packet type %d", pktin->type)); - crStopV; - } - } else { - s->env_ok++; - } + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to X11 forwarding request:" + " packet type %d", pktin->type)); + crStopV; + } + logevent("X11 forwarding refused"); + } else { + logevent("X11 forwarding enabled"); + ssh->X11_fwd_enabled = TRUE; + } + } - s->env_left--; + if (s->requested_agent) { + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to agent forwarding request:" + " packet type %d", pktin->type)); + crStopV; } + logevent("Agent forwarding refused"); + } else { + logevent("Agent forwarding enabled"); + ssh->agentfwd_enabled = TRUE; + } + } + + if (s->requested_tty) { + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to pty request:" + " packet type %d", pktin->type)); + crStopV; + } + c_write_str(ssh, "Server refused to allocate pty\r\n"); + ssh->editing = ssh->echoing = 1; + } else { + logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)", + ssh->ospeed, ssh->ispeed); + ssh->got_pty = TRUE; + } + } else { + ssh->editing = ssh->echoing = 1; + } + + if (s->num_env) { + s->env_ok = 0; + s->env_left = s->num_env; - if (s->env_ok == s->num_env) { - logevent("All environment variables successfully set"); - } else if (s->env_ok == 0) { - logevent("All environment variables refused"); - c_write_str(ssh, "Server refused to set environment variables\r\n"); + while (s->env_left > 0) { + crWaitUntilV(pktin); + + if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) { + if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) { + bombout(("Unexpected response to environment request:" + " packet type %d", pktin->type)); + crStopV; + } } else { - logeventf(ssh, "%d environment variables refused", - s->num_env - s->env_ok); - c_write_str(ssh, "Server refused to set all environment variables\r\n"); + s->env_ok++; } + + s->env_left--; + } + + if (s->env_ok == s->num_env) { + logevent("All environment variables successfully set"); + } else if (s->env_ok == 0) { + logevent("All environment variables refused"); + c_write_str(ssh, "Server refused to set environment variables\r\n"); + } else { + logeventf(ssh, "%d environment variables refused", + s->num_env - s->env_ok); + c_write_str(ssh, "Server refused to set all environment variables\r\n"); } } @@ -9282,6 +9375,25 @@ static void ssh2_msg_debug(Ssh ssh, struct Packet *pktin) logeventf(ssh, "Remote debug message: %.*s", msglen, msg); } +static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin) +{ + do_ssh2_transport(ssh, NULL, 0, pktin); +} + +/* + * Called if we receive a packet that isn't allowed by the protocol. + * This only applies to packets whose meaning PuTTY understands. + * Entirely unknown packets are handled below. + */ +static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin) +{ + char *buf = dupprintf("Server protocol violation: unexpected %s packet", + ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, + pktin->type)); + ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE); + sfree(buf); +} + static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin) { struct Packet *pktout; @@ -9308,45 +9420,47 @@ static void ssh2_protocol_setup(Ssh ssh) ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented; /* - * Any message we actually understand, we set to NULL so that - * the coroutines will get it. + * Initially, we only accept transport messages (and a few generic + * ones). do_ssh2_authconn will add more when it starts. + * Messages that are understood but not currently acceptable go to + * ssh2_msg_unexpected. */ - ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXINIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = NULL; - ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL; - ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = NULL; - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = NULL; duplicate case value */ - /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = NULL; duplicate case value */ - ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = NULL; - ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = NULL; + ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_KEXINIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = ssh2_msg_transport; + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = ssh2_msg_transport; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = ssh2_msg_transport; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = ssh2_msg_transport; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_unexpected; + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_unexpected; duplicate case value */ + /* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_unexpected; duplicate case value */ + ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected; + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected; /* - * These special message types we install handlers for. + * These messages have a special handler from the start. */ ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect; ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */ @@ -9381,24 +9495,12 @@ static void ssh2_protocol(Ssh ssh, void *vin, int inlen, do_ssh2_transport(ssh, "too much data received", -1, NULL); } - if (pktin && ssh->packet_dispatch[pktin->type]) { + if (pktin) ssh->packet_dispatch[pktin->type](ssh, pktin); - return; - } - - if (!ssh->protocol_initial_phase_done || - (pktin && pktin->type >= 20 && pktin->type < 50)) { - if (do_ssh2_transport(ssh, in, inlen, pktin) && - !ssh->protocol_initial_phase_done) { - ssh->protocol_initial_phase_done = TRUE; - /* - * Allow authconn to initialise itself. - */ - do_ssh2_authconn(ssh, NULL, 0, NULL); - } - } else { + else if (!ssh->protocol_initial_phase_done) + do_ssh2_transport(ssh, in, inlen, pktin); + else do_ssh2_authconn(ssh, in, inlen, pktin); - } } static void ssh_cache_conf_values(Ssh ssh) @@ -9460,12 +9562,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->v2_outgoing_sequence = 0; ssh->ssh1_rdpkt_crstate = 0; ssh->ssh2_rdpkt_crstate = 0; - ssh->do_ssh_init_crstate = 0; ssh->ssh_gotdata_crstate = 0; ssh->do_ssh1_connection_crstate = 0; - ssh->do_ssh1_login_crstate = 0; - ssh->do_ssh2_transport_crstate = 0; - ssh->do_ssh2_authconn_crstate = 0; ssh->do_ssh_init_state = NULL; ssh->do_ssh1_login_state = NULL; ssh->do_ssh2_transport_state = NULL; @@ -9999,6 +10097,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)