X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/ab87bf1d328f04e0a71404da891dd95810340b3f..21226fd7e6d5e69dab830cc745e2dc59ed4a00cc:/ssh.c diff --git a/ssh.c b/ssh.c index a2aeb541..d1a2c80f 100644 --- a/ssh.c +++ b/ssh.c @@ -62,6 +62,10 @@ #define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */ #define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */ +#define SSH1_AUTH_RHOSTS 1 /* 0x1 */ +#define SSH1_AUTH_RSA 2 /* 0x2 */ +#define SSH1_AUTH_PASSWORD 3 /* 0x3 */ +#define SSH1_AUTH_RHOSTS_RSA 4 /* 0x4 */ #define SSH1_AUTH_TIS 5 /* 0x5 */ #define SSH1_AUTH_CCARD 16 /* 0x10 */ @@ -529,6 +533,14 @@ enum { /* channel types */ }; /* + * little structure to keep track of outstanding WINDOW_ADJUSTs + */ +struct winadj { + struct winadj *next; + unsigned size; +}; + +/* * 2-3-4 tree storing channels. */ struct ssh_channel { @@ -557,6 +569,18 @@ struct ssh_channel { unsigned remwindow, remmaxpkt; /* locwindow is signed so we can cope with excess data. */ int locwindow, locmaxwin; + /* + * remlocwin is the amount of local window that we think + * the remote end had available to it after it sent the + * last data packet or window adjust ack. + */ + int remlocwin; + /* + * These store the list of window adjusts that haven't + * been acked. + */ + struct winadj *winadj_head, *winadj_tail; + enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; } v2; } v; union { @@ -3066,6 +3090,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin); s->supported_ciphers_mask = ssh_pkt_getuint32(pktin); s->supported_auths_mask = ssh_pkt_getuint32(pktin); + if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) + s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA); ssh->v1_local_protoflags = ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED; @@ -3319,7 +3345,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, crWaitUntil(pktin); - if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) { /* We must not attempt PK auth. Pretend we've already tried it. */ s->tried_publickey = s->tried_agent = 1; } else { @@ -3776,6 +3802,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { + if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) { + bombout(("No supported authentication methods available")); + crStop(0); + } s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ", @@ -6193,7 +6223,51 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) */ if (newwin / 2 >= c->v.v2.locwindow) { struct Packet *pktout; + struct winadj *wa; + /* + * In order to keep track of how much window the client + * actually has available, we'd like it to acknowledge each + * WINDOW_ADJUST. We can't do that directly, so we accompany + * it with a CHANNEL_REQUEST that has to be acknowledged. + * + * This is only necessary if we're opening the window wide. + * If we're not, then throughput is being constrained by + * something other than the maximum window size anyway. + * + * We also only send this if the main channel has finished its + * initial CHANNEL_REQUESTs and installed the default + * CHANNEL_FAILURE handler, so as not to risk giving it + * unexpected CHANNEL_FAILUREs. + */ + if (newwin == c->v.v2.locmaxwin && + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) { + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); + ssh2_pkt_adduint32(pktout, c->remoteid); + ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org"); + ssh2_pkt_addbool(pktout, TRUE); + ssh2_pkt_send(ssh, pktout); + + /* + * CHANNEL_FAILURE doesn't come with any indication of + * what message caused it, so we have to keep track of the + * outstanding CHANNEL_REQUESTs ourselves. + */ + wa = snew(struct winadj); + wa->size = newwin - c->v.v2.locwindow; + wa->next = NULL; + if (!c->v.v2.winadj_head) + c->v.v2.winadj_head = wa; + else + c->v.v2.winadj_tail->next = wa; + c->v.v2.winadj_tail = wa; + if (c->v.v2.throttle_state != UNTHROTTLED) + c->v.v2.throttle_state = UNTHROTTLING; + } else { + /* Pretend the WINDOW_ADJUST was acked immediately. */ + c->v.v2.remlocwin = newwin; + c->v.v2.throttle_state = THROTTLED; + } pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST); ssh2_pkt_adduint32(pktout, c->remoteid); ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow); @@ -6202,6 +6276,38 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin) } } +static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin) +{ + /* + * The only time this should get called is for "winadj@putty" + * messages sent above. All other channel requests are either + * sent with want_reply false or are sent before this handler gets + * installed. + */ + unsigned i = ssh_pkt_getuint32(pktin); + struct ssh_channel *c; + struct winadj *wa; + + c = find234(ssh->channels, &i, ssh_channelfind); + if (!c) + return; /* nonexistent channel */ + wa = c->v.v2.winadj_head; + if (!wa) + logevent("excess SSH_MSG_CHANNEL_FAILURE"); + else { + c->v.v2.winadj_head = wa->next; + c->v.v2.remlocwin += wa->size; + sfree(wa); + /* + * winadj messages are only sent when the window is fully open, + * so if we get an ack of one, we know any pending unthrottle + * is complete. + */ + if (c->v.v2.throttle_state == UNTHROTTLING) + c->v.v2.throttle_state = UNTHROTTLED; + } +} + static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin) { unsigned i = ssh_pkt_getuint32(pktin); @@ -6229,6 +6335,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (data) { int bufsize = 0; c->v.v2.locwindow -= length; + c->v.v2.remlocwin -= length; switch (c->type) { case CHAN_MAINSESSION: bufsize = @@ -6286,6 +6393,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) break; } /* + * If it looks like the remote end hit the end of its window, + * and we didn't want it to do that, think about using a + * larger window. + */ + if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED && + c->v.v2.locmaxwin < 0x40000000) + c->v.v2.locmaxwin += OUR_V2_WINSIZE; + /* * If we are not buffering too much data, * enlarge the window again at the remote side. * If we are buffering too much, we may still @@ -6763,6 +6878,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; c->v.v2.remwindow = winsize; c->v.v2.remmaxpkt = pktsize; + c->v.v2.remlocwin = OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL; + c->v.v2.throttle_state = UNTHROTTLED; bufchain_init(&c->v.v2.outbuffer); add234(ssh->channels, c); pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); @@ -7972,7 +8090,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "direct-tcpip"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + ssh->mainchan->v.v2.winadj_head = NULL; + ssh->mainchan->v.v2.winadj_tail = NULL; + ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host); @@ -8015,7 +8137,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh2_pkt_addstring(s->pktout, "session"); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin = + ssh->mainchan->v.v2.remlocwin = ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; + ssh->mainchan->v.v2.winadj_head = NULL; + ssh->mainchan->v.v2.winadj_tail = NULL; + ssh->mainchan->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_send(ssh, s->pktout); @@ -8329,6 +8455,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh_special(ssh, TS_EOF); /* + * All the initial channel requests are done, so install the default + * failure handler. + */ + ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure; + + /* * Transfer data! */ if (ssh->ldisc) @@ -9108,6 +9240,9 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) ssh2_pkt_addstring(pktout, "direct-tcpip"); ssh2_pkt_adduint32(pktout, c->localid); c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE; + c->v.v2.remlocwin = OUR_V2_WINSIZE; + c->v.v2.winadj_head = c->v.v2.winadj_head = NULL; + c->v.v2.throttle_state = UNTHROTTLED; ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ ssh2_pkt_addstring(pktout, hostname);