X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/088bde77a60867dec8e24141abce80d74711bfae..818ab3bbbe73842da6b50d75ad4a64e40de5e2e6:/ssh.c diff --git a/ssh.c b/ssh.c index 19c93df4..258fd898 100644 --- a/ssh.c +++ b/ssh.c @@ -183,6 +183,7 @@ static const char *const ssh2_disconnect_reasons[] = { #define BUG_CHOKES_ON_RSA 8 #define BUG_SSH2_RSA_PADDING 16 #define BUG_SSH2_DERIVEKEY 32 +#define BUG_SSH2_DH_GEX 64 static int ssh_pkt_ctx = 0; @@ -426,6 +427,16 @@ enum { /* channel types */ struct ssh_channel { unsigned remoteid, localid; int type; + /* + * In SSH1, this value contains four bits: + * + * 1 We have sent SSH1_MSG_CHANNEL_CLOSE. + * 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. + * 4 We have received SSH1_MSG_CHANNEL_CLOSE. + * 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION. + * + * A channel is completely finished with when all four bits are set. + */ int closes; union { struct ssh1_data_channel { @@ -522,6 +533,10 @@ static int savedport; static int ssh_send_ok; static int ssh_echoing, ssh_editing; +static void *frontend; + +static int ssh_term_width, ssh_term_height; + static tree234 *ssh_channels; /* indexed by local id */ static struct ssh_channel *mainchan; /* primary session channel */ static int ssh_exitcode = -1; @@ -558,7 +573,7 @@ static int ssh1_stdout_throttling; static void (*ssh_protocol) (unsigned char *in, int inlen, int ispkt); static void ssh1_protocol(unsigned char *in, int inlen, int ispkt); static void ssh2_protocol(unsigned char *in, int inlen, int ispkt); -static void ssh_size(void); +static void ssh_size(int width, int height); static void ssh_special(Telnet_Special); static int ssh2_try_send(struct ssh_channel *c); static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, @@ -677,7 +692,7 @@ static void c_write(char *buf, int len) fputc(buf[i], stderr); return; } - from_backend(1, buf, len); + from_backend(frontend, 1, buf, len); } static void c_write_untrusted(char *buf, int len) @@ -804,11 +819,11 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen) if (pktin.type == SSH1_MSG_DEBUG) { /* log debug message */ - char buf[80]; + char buf[512]; int stringlen = GET_32BIT(pktin.body); - strcpy(buf, "Remote: "); - if (stringlen > 70) - stringlen = 70; + strcpy(buf, "Remote debug message: "); + if (stringlen > 480) + stringlen = 480; memcpy(buf + 8, pktin.body + 4, stringlen); buf[8 + stringlen] = '\0'; logevent(buf); @@ -1660,9 +1675,11 @@ static void ssh_detect_bugs(char *vstring) ssh_remote_bugs = 0; - if (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || - !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || - !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")) { + if (cfg.sshbug_ignore1 == BUG_ON || + (cfg.sshbug_ignore1 == BUG_AUTO && + (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || + !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") || + !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")))) { /* * These versions don't support SSH1_MSG_IGNORE, so we have * to use a different defence against password length @@ -1672,7 +1689,9 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version has SSH1 ignore bug"); } - if (!strcmp(imp, "Cisco-1.25")) { + if (cfg.sshbug_plainpw1 == BUG_ON || + (cfg.sshbug_plainpw1 == BUG_AUTO && + (!strcmp(imp, "Cisco-1.25")))) { /* * These versions need a plain password sent; they can't * handle having a null and a random length of data after @@ -1682,7 +1701,9 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version needs a plain SSH1 password"); } - if (!strcmp(imp, "Cisco-1.25")) { + if (cfg.sshbug_rsa1 == BUG_ON || + (cfg.sshbug_rsa1 == BUG_AUTO && + (!strcmp(imp, "Cisco-1.25")))) { /* * These versions apparently have no clue whatever about * RSA authentication and will panic and die if they see @@ -1692,9 +1713,11 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version can't handle RSA authentication"); } - if (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) || - !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) || - !strncmp(imp, "2.1 ", 4)) { + if (cfg.sshbug_hmac2 == BUG_ON || + (cfg.sshbug_hmac2 == BUG_AUTO && + (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) || + !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) || + !strncmp(imp, "2.1 ", 4)))) { /* * These versions have the HMAC bug. */ @@ -1702,7 +1725,9 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version has SSH2 HMAC bug"); } - if (!strncmp(imp, "2.0.", 4)) { + if (cfg.sshbug_derivekey2 == BUG_ON || + (cfg.sshbug_derivekey2 == BUG_AUTO && + (!strncmp(imp, "2.0.", 4)))) { /* * These versions have the key-derivation bug (failing to * include the literal shared secret in the hashes that @@ -1712,14 +1737,24 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version has SSH2 key-derivation bug"); } - if ((!strncmp(imp, "OpenSSH_2.", 10) && imp[10]>='5' && imp[10]<='9') || - (!strncmp(imp, "OpenSSH_3.", 10) && imp[10]>='0' && imp[10]<='2')) { + if (cfg.sshbug_rsapad2 == BUG_ON || + (cfg.sshbug_rsapad2 == BUG_AUTO && + ((!strncmp(imp, "OpenSSH_2.", 10) && imp[10]>='5' && imp[10]<='9') || + (!strncmp(imp, "OpenSSH_3.", 10) && imp[10]>='0' && imp[10]<='2')))){ /* * These versions have the SSH2 RSA padding bug. */ ssh_remote_bugs |= BUG_SSH2_RSA_PADDING; logevent("We believe remote version has SSH2 RSA padding bug"); } + + if (cfg.sshbug_dhgex2 == BUG_ON) { + /* + * These versions have the SSH2 DH GEX bug. + */ + ssh_remote_bugs |= BUG_SSH2_DH_GEX; + logevent("We believe remote version has SSH2 DH group exchange bug"); + } } static int do_ssh_init(unsigned char c) @@ -2350,8 +2385,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) c_write_str("\r\n"); } } else { - strncpy(username, cfg.username, 99); - username[99] = '\0'; + strncpy(username, cfg.username, sizeof(username)); + username[sizeof(username)-1] = '\0'; } send_packet(SSH1_CMSG_USER, PKT_STR, username, PKT_END); @@ -2876,7 +2911,7 @@ void sshfwd_close(struct ssh_channel *c) ssh2_pkt_send(); } } - c->closes = 1; + c->closes = 1; /* sent MSG_CLOSE */ if (c->type == CHAN_X11) { c->u.x11.s = NULL; logevent("Forwarded X11 connection terminated"); @@ -3093,7 +3128,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) if (!cfg.nopty) { send_packet(SSH1_CMSG_REQUEST_PTY, PKT_STR, cfg.termtype, - PKT_INT, rows, PKT_INT, cols, + PKT_INT, ssh_term_height, + PKT_INT, ssh_term_width, PKT_INT, 0, PKT_INT, 0, PKT_CHAR, 0, PKT_END); ssh_state = SSH_STATE_INTERMED; do { @@ -3153,7 +3189,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) ssh_state = SSH_STATE_SESSION; if (size_needed) - ssh_size(); + ssh_size(ssh_term_width, ssh_term_height); if (eof_needed) ssh_special(TS_EOF); @@ -3167,7 +3203,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) pktin.type == SSH1_SMSG_STDERR_DATA) { long len = GET_32BIT(pktin.body); int bufsize = - from_backend(pktin.type == SSH1_SMSG_STDERR_DATA, + from_backend(frontend, pktin.type == SSH1_SMSG_STDERR_DATA, pktin.body + 4, len); if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh1_stdout_throttling = 1; @@ -3332,13 +3368,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) unsigned i = GET_32BIT(pktin.body); struct ssh_channel *c; c = find234(ssh_channels, &i, ssh_channelfind); - if (c) { + if (c && ((int)c->remoteid) != -1) { int closetype; closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2); - if (!(c->closes & closetype)) - send_packet(pktin.type, PKT_INT, c->remoteid, - PKT_END); + if ((c->closes == 0) && (c->type == CHAN_X11)) { logevent("Forwarded X11 connection terminated"); assert(c->u.x11.s != NULL); @@ -3351,11 +3385,23 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) pfd_close(c->u.pfd.s); c->u.pfd.s = NULL; } - c->closes |= closetype; - if (c->closes == 3) { + + c->closes |= (closetype << 2); /* seen this message */ + if (!(c->closes & closetype)) { + send_packet(pktin.type, PKT_INT, c->remoteid, + PKT_END); + c->closes |= closetype; /* sent it too */ + } + + if (c->closes == 15) { del234(ssh_channels, c); sfree(c); } + } else { + bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n", + pktin.type == SSH1_MSG_CHANNEL_CLOSE ? "" : + "_CONFIRMATION", c ? "half-open" : "nonexistent", + i)); } } else if (pktin.type == SSH1_MSG_CHANNEL_DATA) { /* Data sent down one of our channels. */ @@ -3601,7 +3647,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) /* * Be prepared to work around the buggy MAC problem. */ - if (cfg.buggymac || (ssh_remote_bugs & BUG_SSH2_HMAC)) + if (ssh_remote_bugs & BUG_SSH2_HMAC) maclist = buggymacs, nmacs = lenof(buggymacs); else maclist = macs, nmacs = lenof(macs); @@ -3616,6 +3662,9 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) /* List key exchange algorithms. */ ssh2_pkt_addstring_start(); for (i = 0; i < lenof(kex_algs); i++) { + if (kex_algs[i] == &ssh_diffiehellman_gex && + (ssh_remote_bugs & BUG_SSH2_DH_GEX)) + continue; ssh2_pkt_addstring_str(kex_algs[i]->name); if (i < lenof(kex_algs) - 1) ssh2_pkt_addstring_str(","); @@ -3722,6 +3771,9 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) pktin.savedpos += 16; /* skip garbage cookie */ ssh2_pkt_getstring(&str, &len); /* key exchange algorithms */ for (i = 0; i < lenof(kex_algs); i++) { + if (kex_algs[i] == &ssh_diffiehellman_gex && + (ssh_remote_bugs & BUG_SSH2_DH_GEX)) + continue; if (in_commasep_string(kex_algs[i]->name, str, len)) { kex = kex_algs[i]; break; @@ -4170,8 +4222,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) username[strcspn(username, "\n\r")] = '\0'; } else { char stuff[200]; - strncpy(username, cfg.username, 99); - username[99] = '\0'; + strncpy(username, cfg.username, sizeof(username)); + username[sizeof(username)-1] = '\0'; if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { sprintf(stuff, "Using username \"%s\".\r\n", username); c_write_str(stuff); @@ -5095,8 +5147,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) ssh2_pkt_addstring("pty-req"); ssh2_pkt_addbool(1); /* want reply */ ssh2_pkt_addstring(cfg.termtype); - ssh2_pkt_adduint32(cols); - ssh2_pkt_adduint32(rows); + ssh2_pkt_adduint32(ssh_term_width); + ssh2_pkt_adduint32(ssh_term_height); ssh2_pkt_adduint32(0); /* pixel width */ ssh2_pkt_adduint32(0); /* pixel height */ ssh2_pkt_addstring_start(); @@ -5201,7 +5253,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) ssh_state = SSH_STATE_SESSION; if (size_needed) - ssh_size(); + ssh_size(ssh_term_width, ssh_term_height); if (eof_needed) ssh_special(TS_EOF); @@ -5234,7 +5286,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) switch (c->type) { case CHAN_MAINSESSION: bufsize = - from_backend(pktin.type == + from_backend(frontend, pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA, data, length); break; @@ -5332,8 +5384,10 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) struct ssh_channel *c; c = find234(ssh_channels, &i, ssh_channelfind); - if (!c) - continue; /* nonexistent channel */ + if (!c || ((int)c->remoteid) == -1) { + bombout(("Received CHANNEL_CLOSE for %s channel %d\n", + c ? "half-open" : "nonexistent", i)); + } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: @@ -5657,7 +5711,8 @@ static void ssh2_protocol(unsigned char *in, int inlen, int ispkt) * * Returns an error message, or NULL on success. */ -static char *ssh_init(char *host, int port, char **realhost, int nodelay) +static char *ssh_init(void *frontend_handle, + char *host, int port, char **realhost, int nodelay) { char *p; @@ -5666,6 +5721,10 @@ static char *ssh_init(char *host, int port, char **realhost, int nodelay) return "Microsoft high encryption pack not installed!"; #endif + frontend = frontend_handle; + ssh_term_width = cfg.width; + ssh_term_height = cfg.height; + ssh_send_ok = 0; ssh_editing = 0; ssh_echoing = 0; @@ -5726,8 +5785,11 @@ static int ssh_sendbuffer(void) /* * Called to set the size of the window from SSH's POV. */ -static void ssh_size(void) +static void ssh_size(int width, int height) { + ssh_term_width = width; + ssh_term_height = height; + switch (ssh_state) { case SSH_STATE_BEFORE_SIZE: case SSH_STATE_PREPACKET: @@ -5738,17 +5800,19 @@ static void ssh_size(void) break; case SSH_STATE_SESSION: if (!cfg.nopty) { + if (!term) + return; if (ssh_version == 1) { send_packet(SSH1_CMSG_WINDOW_SIZE, - PKT_INT, rows, PKT_INT, cols, + PKT_INT, ssh_term_height, PKT_INT, ssh_term_width, PKT_INT, 0, PKT_INT, 0, PKT_END); } else { ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(mainchan->remoteid); ssh2_pkt_addstring("window-change"); ssh2_pkt_addbool(0); - ssh2_pkt_adduint32(cols); - ssh2_pkt_adduint32(rows); + ssh2_pkt_adduint32(ssh_term_width); + ssh2_pkt_adduint32(ssh_term_height); ssh2_pkt_adduint32(0); ssh2_pkt_adduint32(0); ssh2_pkt_send(); @@ -5787,7 +5851,8 @@ static void ssh_special(Telnet_Special code) if (ssh_state == SSH_STATE_CLOSED || ssh_state == SSH_STATE_PREPACKET) return; if (ssh_version == 1) { - send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END); + if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) + send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END); } else { ssh2_pkt_init(SSH2_MSG_IGNORE); ssh2_pkt_addstring_start();