X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/760e88b21a672b2a4bb96c4c1bc041758eac1677..e52455b1fd4e5a129cebe653a777b1f421b18b4f:/ssh.c diff --git a/ssh.c b/ssh.c index 5c10bc80..119cd4fe 100644 --- a/ssh.c +++ b/ssh.c @@ -159,6 +159,8 @@ static const char *const ssh2_disconnect_reasons[] = { */ #define BUG_CHOKES_ON_SSH1_IGNORE 1 #define BUG_SSH2_HMAC 2 +#define BUG_NEEDS_SSH1_PLAIN_PASSWORD 4 + #define GET_32BIT(cp) \ (((unsigned long)(unsigned char)(cp)[0] << 24) | \ @@ -940,23 +942,18 @@ static int s_wrpkt_prepare(void) pktout.body[-1] = pktout.type; -#ifdef DUMP_PACKETS - debug(("Packet payload pre-compression:\n")); - dmemdump(pktout.body - 1, pktout.length + 1); -#endif - if (ssh1_compressing) { unsigned char *compblk; int complen; +#ifdef DUMP_PACKETS + debug(("Packet payload pre-compression:\n")); + dmemdump(pktout.body - 1, pktout.length + 1); +#endif zlib_compress_block(pktout.body - 1, pktout.length + 1, &compblk, &complen); ssh1_pktout_size(complen - 1); memcpy(pktout.body - 1, compblk, complen); sfree(compblk); -#ifdef DUMP_PACKETS - debug(("Packet payload post-compression:\n")); - dmemdump(pktout.body - 1, pktout.length + 1); -#endif } len = pktout.length + 5; /* type and CRC */ @@ -1239,13 +1236,15 @@ static int ssh2_pkt_construct(void) /* * Compress packet payload. */ -#ifdef DUMP_PACKETS - debug(("Pre-compression payload:\n")); - dmemdump(pktout.data + 5, pktout.length - 5); -#endif { unsigned char *newpayload; int newlen; +#ifdef DUMP_PACKETS + if (cscomp && cscomp != &ssh_comp_none) { + debug(("Pre-compression payload:\n")); + dmemdump(pktout.data + 5, pktout.length - 5); + } +#endif if (cscomp && cscomp->compress(pktout.data + 5, pktout.length - 5, &newpayload, &newlen)) { pktout.length = 5; @@ -1418,17 +1417,15 @@ static void ssh_detect_bugs(char *vstring) char *imp; /* pointer to implementation part */ imp = vstring; imp += strcspn(imp, "-"); - if (*imp) - imp++; + if (*imp) imp++; imp += strcspn(imp, "-"); - if (*imp) - imp++; + if (*imp) imp++; 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, "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 @@ -1438,6 +1435,16 @@ static void ssh_detect_bugs(char *vstring) logevent("We believe remote version has SSH1 ignore bug"); } + if (!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 + * the password. + */ + ssh_remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD; + logevent("We believe remote version needs a plain SSH1 password"); + } + 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)) { @@ -1479,8 +1486,8 @@ static int do_ssh_init(unsigned char c) crReturn(1); /* get another character */ } - vstring = smalloc(16); vstrsize = 16; + vstring = smalloc(vstrsize); strcpy(vstring, "SSH-"); vslen = 4; i = 0; @@ -1506,10 +1513,10 @@ static int do_ssh_init(unsigned char c) vstring[vslen] = 0; vlog = smalloc(20 + vslen); + vstring[strcspn (vstring, "\r\n")] = '\0'; /* remove end-of-line chars */ sprintf(vlog, "Server version: %s", vstring); - ssh_detect_bugs(vstring); - vlog[strcspn(vlog, "\r\n")] = '\0'; logevent(vlog); + ssh_detect_bugs(vstring); sfree(vlog); /* @@ -2405,26 +2412,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) * use of the fact that the password is interpreted * as a C string: so we can append a NUL, then some * random data. + * + * One server (a Cisco one) can deal with neither + * SSH1_MSG_IGNORE _nor_ a padded password string. + * For this server we are left with no defences + * against password length sniffing. */ - if (ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) { - char string[64]; - char *s; - int len; - - len = strlen(password); - if (len < sizeof(string)) { - s = string; - strcpy(string, password); - len++; /* cover the zero byte */ - while (len < sizeof(string)) { - string[len++] = (char) random_byte(); - } - } else { - s = password; - } - send_packet(pwpkt_type, PKT_INT, len, - PKT_DATA, s, len, PKT_END); - } else { + if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { + /* + * The server can deal with SSH1_MSG_IGNORE, so + * we can use the primary defence. + */ int bottom, top, pwlen, i; char *randomstr; @@ -2456,7 +2454,45 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) PKT_STR, randomstr, PKT_END); } } + logevent("Sending password with camouflage packets"); ssh_pkt_defersend(); + } + else if (!(ssh_remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) { + /* + * The server can't deal with SSH1_MSG_IGNORE + * but can deal with padded passwords, so we + * can use the secondary defence. + */ + char string[64]; + char *s; + int len; + + len = strlen(password); + if (len < sizeof(string)) { + s = string; + strcpy(string, password); + len++; /* cover the zero byte */ + while (len < sizeof(string)) { + string[len++] = (char) random_byte(); + } + } else { + s = password; + } + logevent("Sending length-padded password"); + send_packet(pwpkt_type, PKT_INT, len, + PKT_DATA, s, len, PKT_END); + } else { + /* + * The server has _both_ + * BUG_CHOKES_ON_SSH1_IGNORE and + * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is + * therefore nothing we can do. + */ + int len; + len = strlen(password); + logevent("Sending unpadded password"); + send_packet(pwpkt_type, PKT_INT, len, + PKT_DATA, password, len, PKT_END); } } else { send_packet(pwpkt_type, PKT_STR, password, PKT_END); @@ -2487,19 +2523,29 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) void sshfwd_close(struct ssh_channel *c) { if (c && !c->closes) { - if (ssh_version == 1) { - send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, - PKT_END); - } else { - ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(c->remoteid); - ssh2_pkt_send(); + /* + * If the channel's remoteid is -1, we have sent + * CHANNEL_OPEN for this channel, but it hasn't even been + * acknowledged by the server. So we must set a close flag + * on it now, and then when the server acks the channel + * open, we can close it then. + */ + if (c->remoteid != -1) { + if (ssh_version == 1) { + send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + } else { + ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(c->remoteid); + ssh2_pkt_send(); + } } c->closes = 1; if (c->type == CHAN_X11) { c->u.x11.s = NULL; logevent("Forwarded X11 connection terminated"); - } else if (c->type == CHAN_SOCKDATA) { + } else if (c->type == CHAN_SOCKDATA || + c->type == CHAN_SOCKDATA_DORMANT) { c->u.pfd.s = NULL; logevent("Forwarded port closed"); } @@ -2869,6 +2915,17 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) pfd_confirm(c->u.pfd.s); } + if (c && c->closes) { + /* + * We have a pending close on this channel, + * which we decided on before the server acked + * the channel open. So now we know the + * remoteid, we can close it again. + */ + send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, + PKT_END); + } + } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) { unsigned int remoteid = GET_32BIT(pktin.body); unsigned int localid = GET_32BIT(pktin.body+4); @@ -4379,7 +4436,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Server got confused by X11 forwarding request")); + bombout(("Unexpected response to X11 forwarding request:" + " packet type %d", pktin.type)); crReturnV; } logevent("X11 forwarding refused"); @@ -4467,7 +4525,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH2_MSG_REQUEST_SUCCESS) { if (pktin.type != SSH2_MSG_REQUEST_FAILURE) { - bombout(("Server got confused by port forwarding request")); + bombout(("Unexpected response to port " + "forwarding request: packet type %d", + pktin.type)); crReturnV; } logevent("Server refused this port forwarding"); @@ -4505,8 +4565,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { - bombout( - ("Server got confused by agent forwarding request")); + bombout(("Unexpected response to agent forwarding request:" + " packet type %d", pktin.type)); crReturnV; } logevent("Agent forwarding refused"); @@ -4548,7 +4608,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Server got confused by pty request")); + bombout(("Unexpected response to pty request:" + " packet type %d", pktin.type)); crReturnV; } c_write_str("Server refused to allocate pty\r\n"); @@ -4605,7 +4666,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST); if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) { if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) { - bombout(("Server got confused by shell/command request")); + bombout(("Unexpected response to shell/command request:" + " packet type %d", pktin.type)); crReturnV; } /* @@ -4823,11 +4885,22 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) continue; /* dunno why they're confirming this */ c->remoteid = ssh2_pkt_getuint32(); c->type = CHAN_SOCKDATA; - c->closes = 0; c->v.v2.remwindow = ssh2_pkt_getuint32(); c->v.v2.remmaxpkt = ssh2_pkt_getuint32(); bufchain_init(&c->v.v2.outbuffer); - pfd_confirm(c->u.pfd.s); + if (c->u.pfd.s) + pfd_confirm(c->u.pfd.s); + if (c->closes) { + /* + * We have a pending close on this channel, + * which we decided on before the server acked + * the channel open. So now we know the + * remoteid, we can close it again. + */ + ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(c->remoteid); + ssh2_pkt_send(); + } } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) { unsigned i = ssh2_pkt_getuint32(); struct ssh_channel *c;