X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/b3e9b21a1cb36230bd1507649ccfb15f66bdd524..aacc46fab3288bb9ac32c5d50ccfb9c3ecbfd95d:/ssh.c diff --git a/ssh.c b/ssh.c index 846aa7ce..30ee4ba8 100644 --- a/ssh.c +++ b/ssh.c @@ -525,7 +525,7 @@ static void ssh_channel_destroy(struct ssh_channel *c); const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss }; const static struct ssh_mac *macs[] = { - &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 + &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static struct ssh_mac *buggymacs[] = { &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 @@ -666,6 +666,7 @@ struct ssh_channel { unsigned char *message; unsigned char msglen[4]; unsigned lensofar, totallen; + int outstanding_requests; } a; struct ssh_x11_channel { Socket s; @@ -771,7 +772,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit); static unsigned long ssh_pkt_getuint32(struct Packet *pkt); static int ssh2_pkt_getbool(struct Packet *pkt); static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length); -static void ssh2_timer(void *ctx, long now); +static void ssh2_timer(void *ctx, unsigned long now); static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, struct Packet *pktin); static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin); @@ -980,7 +981,7 @@ struct ssh_tag { unsigned long incoming_data_size, outgoing_data_size, deferred_data_size; unsigned long max_data_size; int kex_in_progress; - long next_rekey, last_rekey; + unsigned long next_rekey, last_rekey; char *deferred_rekey_reason; /* points to STATIC string; don't free */ /* @@ -3201,6 +3202,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) Ssh ssh = c->ssh; void *sentreply = reply; + c->u.a.outstanding_requests--; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ sentreply = "\0\0\0\1\5"; @@ -3220,6 +3222,12 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) } if (reply) sfree(reply); + /* + * If we've already seen an incoming EOF but haven't sent an + * outgoing one, this may be the moment to send it. + */ + if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF)) + sshfwd_write_eof(c); } /* @@ -3263,7 +3271,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, { int i, j, ret; unsigned char cookie[8], *ptr; - struct RSAKey servkey, hostkey; struct MD5Context md5c; struct do_ssh1_login_state { int crLine; @@ -3291,6 +3298,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int commentlen; int dlgret; Filename *keyfile; + struct RSAKey servkey, hostkey; }; crState(do_ssh1_login_state); @@ -3313,8 +3321,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, } memcpy(cookie, ptr, 8); - if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) || - !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) { + if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) || + !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) { bombout(("Failed to read SSH-1 public keys from public key packet")); crStop(0); } @@ -3326,9 +3334,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, char logmsg[80]; logevent("Host key fingerprint is:"); strcpy(logmsg, " "); - hostkey.comment = NULL; + s->hostkey.comment = NULL; rsa_fingerprint(logmsg + strlen(logmsg), - sizeof(logmsg) - strlen(logmsg), &hostkey); + sizeof(logmsg) - strlen(logmsg), &s->hostkey); logevent(logmsg); } @@ -3343,8 +3351,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER; MD5Init(&md5c); - MD5Update(&md5c, s->keystr2, hostkey.bytes); - MD5Update(&md5c, s->keystr1, servkey.bytes); + MD5Update(&md5c, s->keystr2, s->hostkey.bytes); + MD5Update(&md5c, s->keystr1, s->servkey.bytes); MD5Update(&md5c, cookie, 8); MD5Final(s->session_id, &md5c); @@ -3354,13 +3362,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * Verify that the `bits' and `bytes' parameters match. */ - if (hostkey.bits > hostkey.bytes * 8 || - servkey.bits > servkey.bytes * 8) { + if (s->hostkey.bits > s->hostkey.bytes * 8 || + s->servkey.bits > s->servkey.bytes * 8) { bombout(("SSH-1 public keys were badly formatted")); crStop(0); } - s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes); + s->len = (s->hostkey.bytes > s->servkey.bytes ? + s->hostkey.bytes : s->servkey.bytes); s->rsabuf = snewn(s->len, unsigned char); @@ -3371,11 +3380,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, /* * First format the key into a string. */ - int len = rsastr_len(&hostkey); + int len = rsastr_len(&s->hostkey); char fingerprint[100]; char *keystr = snewn(len, char); - rsastr_fmt(keystr, &hostkey); - rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey); + rsastr_fmt(keystr, &s->hostkey); + rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); ssh_set_frozen(ssh, 1); s->dlgret = verify_ssh_host_key(ssh->frontend, @@ -3409,14 +3418,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, s->rsabuf[i] ^= s->session_id[i]; } - if (hostkey.bytes > servkey.bytes) { - ret = rsaencrypt(s->rsabuf, 32, &servkey); + if (s->hostkey.bytes > s->servkey.bytes) { + ret = rsaencrypt(s->rsabuf, 32, &s->servkey); if (ret) - ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey); + ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey); } else { - ret = rsaencrypt(s->rsabuf, 32, &hostkey); + ret = rsaencrypt(s->rsabuf, 32, &s->hostkey); if (ret) - ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey); + ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey); } if (!ret) { bombout(("SSH-1 public key encryptions failed due to bad formatting")); @@ -3519,21 +3528,21 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, ssh->crcda_ctx = crcda_make_context(); logevent("Installing CRC compensation attack detector"); - if (servkey.modulus) { - sfree(servkey.modulus); - servkey.modulus = NULL; + if (s->servkey.modulus) { + sfree(s->servkey.modulus); + s->servkey.modulus = NULL; } - if (servkey.exponent) { - sfree(servkey.exponent); - servkey.exponent = NULL; + if (s->servkey.exponent) { + sfree(s->servkey.exponent); + s->servkey.exponent = NULL; } - if (hostkey.modulus) { - sfree(hostkey.modulus); - hostkey.modulus = NULL; + if (s->hostkey.modulus) { + sfree(s->hostkey.modulus); + s->hostkey.modulus = NULL; } - if (hostkey.exponent) { - sfree(hostkey.exponent); - hostkey.exponent = NULL; + if (s->hostkey.exponent) { + sfree(s->hostkey.exponent); + s->hostkey.exponent = NULL; } crWaitUntil(pktin); @@ -4610,9 +4619,9 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf) /* XXX: rport_acceptall may not represent * what was used to open the original connection, * since it's reconfigurable. */ - ssh2_pkt_addstring(pktout, "0.0.0.0"); + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4725,9 +4734,9 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf) if (epf->saddr) { ssh2_pkt_addstring(pktout, epf->saddr); } else if (conf_get_int(conf, CONF_rport_acceptall)) { - ssh2_pkt_addstring(pktout, "0.0.0.0"); + ssh2_pkt_addstring(pktout, ""); } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); + ssh2_pkt_addstring(pktout, "localhost"); } ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); @@ -4827,6 +4836,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; c->u.a.message = NULL; + c->u.a.outstanding_requests = 0; add234(ssh->channels, c); send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, @@ -5052,6 +5062,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, @@ -6572,6 +6583,21 @@ static void ssh2_channel_init(struct ssh_channel *c) } /* + * Construct the common parts of a CHANNEL_OPEN. + */ +static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type) +{ + struct Packet *pktout; + + pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); + ssh2_pkt_addstring(pktout, type); + ssh2_pkt_adduint32(pktout, c->localid); + ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ + ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + return 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. @@ -6832,6 +6858,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar == c->u.a.totallen) { void *reply; int replylen; + c->u.a.outstanding_requests++; if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, @@ -6970,8 +6997,10 @@ static void ssh2_channel_got_eof(struct ssh_channel *c) if (c->type == CHAN_X11) { x11_send_eof(c->u.x11.s); } else if (c->type == CHAN_AGENT) { - /* Manufacture an outgoing EOF in response to the incoming one. */ - sshfwd_write_eof(c); + if (c->u.a.outstanding_requests == 0) { + /* Manufacture an outgoing EOF in response to the incoming one. */ + sshfwd_write_eof(c); + } } else if (c->type == CHAN_SOCKDATA) { pfd_send_eof(c->u.pfd.s); } else if (c->type == CHAN_MAINSESSION) { @@ -7393,6 +7422,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) else { c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; + c->u.a.outstanding_requests = 0; } } else { error = "Unsupported channel type requested"; @@ -9101,66 +9131,30 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, */ if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) { ssh->mainchan = NULL; - } else if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) { - /* - * Just start a direct-tcpip channel and use it as the main - * channel. - */ + } else { ssh->mainchan = snew(struct ssh_channel); ssh->mainchan->ssh = ssh; ssh2_channel_init(ssh->mainchan); - logeventf(ssh, - "Opening direct-tcpip channel to %s:%d in place of session", - conf_get_str(ssh->conf, CONF_ssh_nc_host), - conf_get_int(ssh->conf, CONF_ssh_nc_port)); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); - ssh2_pkt_addstring(s->pktout, "direct-tcpip"); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - 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, conf_get_str(ssh->conf, CONF_ssh_nc_host)); - ssh2_pkt_adduint32(s->pktout, conf_get_int(ssh->conf, CONF_ssh_nc_port)); - /* - * There's nothing meaningful to put in the originator - * fields, but some servers insist on syntactically correct - * information. - */ - ssh2_pkt_addstring(s->pktout, "0.0.0.0"); - ssh2_pkt_adduint32(s->pktout, 0); - ssh2_pkt_send(ssh, s->pktout); - crWaitUntilV(pktin); - if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a direct-tcpip channel")); - crStopV; - /* FIXME: error data comes back in FAILURE packet */ - } - if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) { - bombout(("Server's channel confirmation cited wrong channel")); - crStopV; + if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) { + /* + * Just start a direct-tcpip channel and use it as the main + * channel. + */ + ssh_send_port_open(ssh->mainchan, + conf_get_str(ssh->conf, CONF_ssh_nc_host), + conf_get_int(ssh->conf, CONF_ssh_nc_port), + "main channel"); + ssh->ncmode = TRUE; + } else { + s->pktout = ssh2_chanopen_init(ssh->mainchan, "session"); + logevent("Opening session as main channel"); + ssh2_pkt_send(ssh, s->pktout); + ssh->ncmode = FALSE; } - ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); - ssh->mainchan->halfopen = FALSE; - ssh->mainchan->type = CHAN_MAINSESSION; - ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); - ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); - add234(ssh->channels, ssh->mainchan); - update_specials_menu(ssh->frontend); - logevent("Opened direct-tcpip channel"); - ssh->ncmode = TRUE; - } else { - ssh->mainchan = snew(struct ssh_channel); - ssh->mainchan->ssh = ssh; - ssh2_channel_init(ssh->mainchan); - s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); - ssh2_pkt_addstring(s->pktout, "session"); - ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid); - 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); crWaitUntilV(pktin); if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { - bombout(("Server refused to open a session")); + bombout(("Server refused to open channel")); crStopV; /* FIXME: error data comes back in FAILURE packet */ } @@ -9175,8 +9169,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); - logevent("Opened channel for session"); - ssh->ncmode = FALSE; + logevent("Opened main channel"); } /* @@ -9490,7 +9483,7 @@ static void ssh2_protocol_setup(Ssh ssh) ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug; } -static void ssh2_timer(void *ctx, long now) +static void ssh2_timer(void *ctx, unsigned long now) { Ssh ssh = (Ssh)ctx; @@ -9498,7 +9491,7 @@ static void ssh2_timer(void *ctx, long now) return; if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 && - now - ssh->next_rekey >= 0) { + now == ssh->next_rekey) { do_ssh2_transport(ssh, "timeout", -1, NULL); } } @@ -9693,7 +9686,7 @@ static void ssh_free(void *handle) while (ssh->qhead) { struct queued_handler *qh = ssh->qhead; ssh->qhead = qh->next; - sfree(ssh->qhead); + sfree(qh); } ssh->qhead = ssh->qtail = NULL; @@ -9781,10 +9774,10 @@ static void ssh_reconfig(void *handle, Conf *conf) rekey_time = conf_get_int(conf, CONF_ssh_rekey_time); if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time && rekey_time != 0) { - long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC; - long now = GETTICKCOUNT(); + unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC; + unsigned long now = GETTICKCOUNT(); - if (new_next - now < 0) { + if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) { rekeying = "timeout shortened"; } else { ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh); @@ -10137,7 +10130,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) Ssh ssh = c->ssh; struct Packet *pktout; - logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port); + logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org); if (ssh->version == 1) { send_packet(ssh, SSH1_MSG_PORT_OPEN, @@ -10147,11 +10140,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) /* PKT_STR, , */ PKT_END); } else { - pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN); - ssh2_pkt_addstring(pktout, "direct-tcpip"); - ssh2_pkt_adduint32(pktout, c->localid); - ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */ - ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ + pktout = ssh2_chanopen_init(c, "direct-tcpip"); ssh2_pkt_addstring(pktout, hostname); ssh2_pkt_adduint32(pktout, port); /*