X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/fd5e5847eb0f834ce8124f0574bd3cfaf878db53..d6dcf6c6407692f1f138f6151e6b24681e103807:/ssh.c diff --git a/ssh.c b/ssh.c index 2c538239..cc6ee309 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) | \ @@ -507,7 +509,7 @@ static int ssh_rportcmp_ssh2(void *av, void *bv) { struct ssh_rportfwd *a = (struct ssh_rportfwd *) av; struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv; - int i; + if (a->sport > b->sport) return +1; if (a->sport < b->sport) @@ -607,12 +609,6 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen) (*data)++, (*datalen)--; } -#ifdef FWHACK - if (st->len == 0x52656d6f) { /* "Remo"te server has closed ... */ - st->len = 0x300; /* big enough to carry to end */ - } -#endif - st->pad = 8 - (st->len % 8); st->biglen = st->len + st->pad; pktin.length = st->len - 5; @@ -725,6 +721,8 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen) memcpy(buf + nowlen, pktin.body + 4, msglen); buf[nowlen + msglen] = '\0'; logevent(buf); + bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen)); + crReturn(0); } crFinish(0); @@ -768,11 +766,7 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen) pktin.data[st->i] = *(*data)++; (*datalen)--; } -#ifdef FWHACK - if (!memcmp(pktin.data, "Remo", 4)) { /* "Remo"te server has closed ... */ - /* FIXME */ - } -#endif + if (sccipher) sccipher->decrypt(pktin.data, st->cipherblk); @@ -898,6 +892,12 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen) memcpy(buf + nowlen, pktin.data + 14, msglen); buf[nowlen + msglen] = '\0'; logevent(buf); + bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"", + reason, + (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ? + ssh2_disconnect_reasons[reason] : "unknown", + buf+nowlen)); + crReturn(0); } crFinish(0); @@ -1420,17 +1420,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 @@ -1440,6 +1438,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)) { @@ -1481,8 +1489,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; @@ -1508,10 +1516,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); /* @@ -1594,6 +1602,9 @@ static void ssh_gotdata(unsigned char *data, int datalen) while (1) { while (datalen > 0) { if (s_rdpkt(&data, &datalen) == 0) { + if (ssh_state == SSH_STATE_CLOSED) { + return; + } ssh_protocol(NULL, 0, 1); if (ssh_state == SSH_STATE_CLOSED) { return; @@ -1686,6 +1697,11 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Try to find host. */ + { + char buf[200]; + sprintf(buf, "Looking up host \"%.170s\"", host); + logevent(buf); + } addr = sk_namelookup(host, realhost); if ((err = sk_addr_error(addr))) return err; @@ -1697,6 +1713,12 @@ static char *connect_to_host(char *host, int port, char **realhost) /* * Open socket. */ + { + char buf[200], addrbuf[100]; + sk_getaddr(addr, addrbuf, 100); + sprintf(buf, "Connecting to %.100s port %d", addrbuf, port); + logevent(buf); + } s = sk_new(addr, port, 0, 1, &fn_table_ptr); if ((err = sk_socket_error(s))) return err; @@ -1777,6 +1799,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) struct MD5Context md5c; static unsigned long supported_ciphers_mask, supported_auths_mask; static int tried_publickey; + static int tis_auth_refused, ccard_auth_refused; static unsigned char session_id[16]; static int cipher_type; static char username[100]; @@ -2025,6 +2048,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) crWaitUntil(ispkt); tried_publickey = 0; + tis_auth_refused = ccard_auth_refused = 0; while (pktin.type == SSH1_SMSG_FAILURE) { static char password[100]; @@ -2032,12 +2056,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) static int pos; static char c; static int pwpkt_type; - /* - * Show password prompt, having first obtained it via a TIS - * or CryptoCard exchange if we're doing TIS or CryptoCard - * authentication. - */ pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; + if (agent_exists()) { /* * Attempt RSA authentication using Pageant. @@ -2160,9 +2180,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) if (*cfg.keyfile && !tried_publickey) pwpkt_type = SSH1_CMSG_AUTH_RSA; - if (pktin.type == SSH1_SMSG_FAILURE && - cfg.try_tis_auth && - (supported_auths_mask & (1 << SSH1_AUTH_TIS))) { + if (cfg.try_tis_auth && + (supported_auths_mask & (1 << SSH1_AUTH_TIS)) && + !tis_auth_refused) { pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; logevent("Requested TIS authentication"); send_packet(SSH1_CMSG_AUTH_TIS, PKT_END); @@ -2171,6 +2191,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) logevent("TIS authentication declined"); if (flags & FLAG_INTERACTIVE) c_write_str("TIS authentication refused.\r\n"); + tis_auth_refused = 1; + continue; } else { int challengelen = ((pktin.body[0] << 24) | (pktin.body[1] << 16) | @@ -2180,12 +2202,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) if (challengelen > sizeof(prompt) - 1) challengelen = sizeof(prompt) - 1; /* prevent overrun */ memcpy(prompt, pktin.body + 4, challengelen); - prompt[challengelen] = '\0'; + /* Prompt heuristic comes from OpenSSH */ + strncpy(prompt + challengelen, + memchr(prompt, '\n', challengelen) ? + "": "\r\nResponse: ", + (sizeof prompt) - challengelen); + prompt[(sizeof prompt) - 1] = '\0'; } } - if (pktin.type == SSH1_SMSG_FAILURE && - cfg.try_tis_auth && - (supported_auths_mask & (1 << SSH1_AUTH_CCARD))) { + if (cfg.try_tis_auth && + (supported_auths_mask & (1 << SSH1_AUTH_CCARD)) && + !ccard_auth_refused) { pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; logevent("Requested CryptoCard authentication"); send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END); @@ -2193,6 +2220,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) { logevent("CryptoCard authentication declined"); c_write_str("CryptoCard authentication refused.\r\n"); + ccard_auth_refused = 1; + continue; } else { int challengelen = ((pktin.body[0] << 24) | (pktin.body[1] << 16) | @@ -2202,7 +2231,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) if (challengelen > sizeof(prompt) - 1) challengelen = sizeof(prompt) - 1; /* prevent overrun */ memcpy(prompt, pktin.body + 4, challengelen); - strncpy(prompt + challengelen, "\r\nResponse : ", + strncpy(prompt + challengelen, + memchr(prompt, '\n', challengelen) ? + "" : "\r\nResponse: ", sizeof(prompt) - challengelen); prompt[sizeof(prompt) - 1] = '\0'; } @@ -2224,6 +2255,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) sfree(comment); } + /* + * Show password prompt, having first obtained it via a TIS + * or CryptoCard exchange if we're doing TIS or CryptoCard + * authentication. + */ if (ssh_get_line) { if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) { /* @@ -2239,7 +2275,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) crReturn(1); } } else { - c_write_str(prompt); + /* Prompt may have come from server. We've munged it a bit, so + * we know it to be zero-terminated at least once. */ + c_write_untrusted(prompt, strlen(prompt)); pos = 0; ssh_send_ok = 1; while (pos >= 0) { @@ -2288,7 +2326,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt) tried_publickey = 1; i = loadrsakey(cfg.keyfile, &pubkey, password); if (i == 0) { - c_write_str("Couldn't load public key from "); + c_write_str("Couldn't load private key from "); c_write_str(cfg.keyfile); c_write_str(".\r\n"); continue; /* go and try password */ @@ -2377,26 +2415,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; @@ -2428,7 +2457,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); @@ -2697,7 +2764,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) if (eof_needed) ssh_special(TS_EOF); - ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ ssh_send_ok = 1; ssh_channels = newtree234(ssh_channelcmp); while (1) { @@ -2709,7 +2776,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) int bufsize = from_backend(pktin.type == SSH1_SMSG_STDERR_DATA, pktin.body + 4, len); - if (bufsize > SSH1_BUFFER_LIMIT) { + if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { ssh1_stdout_throttling = 1; ssh1_throttle(+1); } @@ -2841,6 +2908,19 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) pfd_confirm(c->u.pfd.s); } + } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) { + unsigned int remoteid = GET_32BIT(pktin.body); + unsigned int localid = GET_32BIT(pktin.body+4); + struct ssh_channel *c; + + c = find234(ssh_channels, &remoteid, ssh_channelfind); + if (c && c->type == CHAN_SOCKDATA_DORMANT) { + logevent("Forwarded connection refused by server"); + pfd_close(c->u.pfd.s); + del234(ssh_channels, c); + sfree(c); + } + } else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE || pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) { /* Remote side closes a channel. */ @@ -2942,7 +3022,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) bufsize = 0; /* agent channels never back up */ break; } - if (bufsize > SSH1_BUFFER_LIMIT) { + if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) { c->v.v1.throttling = 1; ssh1_throttle(+1); } @@ -2954,6 +3034,15 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) * if no pty is available or in other odd cases. Ignore */ } else if (pktin.type == SSH1_SMSG_EXIT_STATUS) { send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END); + /* + * In case `helpful' firewalls or proxies tack + * extra human-readable text on the end of the + * session which we might mistake for another + * encrypted packet, we close the session once + * we've sent EXIT_CONFIRMATION. + */ + ssh_state = SSH_STATE_CLOSED; + crReturnV; } else { bombout(("Strange packet received: type %d", pktin.type)); crReturnV; @@ -3793,66 +3882,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) in_commasep_string("publickey", methods, methlen); can_passwd = in_commasep_string("password", methods, methlen); - can_passwd = - in_commasep_string("password", methods, methlen); can_keyb_inter = in_commasep_string("keyboard-interactive", methods, methlen); } method = 0; - if (!method && can_keyb_inter && !tried_keyb_inter) { - method = AUTH_KEYBOARD_INTERACTIVE; - type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - tried_keyb_inter = TRUE; - - ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - ssh2_pkt_addstring(username); - ssh2_pkt_addstring("ssh-connection"); /* service requested */ - ssh2_pkt_addstring("keyboard-interactive"); /* method */ - ssh2_pkt_addstring(""); /* lang */ - ssh2_pkt_addstring(""); - ssh2_pkt_send(); - - crWaitUntilV(ispkt); - if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) { - if (pktin.type == SSH2_MSG_USERAUTH_FAILURE) - gotit = TRUE; - logevent("Keyboard-interactive authentication refused"); - type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET; - continue; - } - - kbd_inter_running = TRUE; - } - - if (kbd_inter_running) { - method = AUTH_KEYBOARD_INTERACTIVE; - type = AUTH_TYPE_KEYBOARD_INTERACTIVE; - tried_keyb_inter = TRUE; - - /* We've got packet with that "interactive" info - dump banners, and set its prompt as ours */ - { - char *name, *inst, *lang, *prompt; - int name_len, inst_len, lang_len, prompt_len; - ssh2_pkt_getstring(&name, &name_len); - ssh2_pkt_getstring(&inst, &inst_len); - ssh2_pkt_getstring(&lang, &lang_len); - if (name_len > 0) - c_write_untrusted(name, name_len); - if (inst_len > 0) - c_write_untrusted(inst, inst_len); - num_prompts = ssh2_pkt_getuint32(); - - ssh2_pkt_getstring(&prompt, &prompt_len); - strncpy(pwprompt, prompt, sizeof(pwprompt)); - need_pw = TRUE; - - echo = ssh2_pkt_getbool(); - } - } - if (!method && can_pubkey && agent_exists() && !tried_agent) { /* * Attempt public-key authentication using Pageant. @@ -4046,6 +4081,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) } } + if (!method && can_keyb_inter && !tried_keyb_inter) { + method = AUTH_KEYBOARD_INTERACTIVE; + type = AUTH_TYPE_KEYBOARD_INTERACTIVE; + tried_keyb_inter = TRUE; + + ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); + ssh2_pkt_addstring(username); + ssh2_pkt_addstring("ssh-connection"); /* service requested */ + ssh2_pkt_addstring("keyboard-interactive"); /* method */ + ssh2_pkt_addstring(""); /* lang */ + ssh2_pkt_addstring(""); + ssh2_pkt_send(); + + crWaitUntilV(ispkt); + if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) { + if (pktin.type == SSH2_MSG_USERAUTH_FAILURE) + gotit = TRUE; + logevent("Keyboard-interactive authentication refused"); + type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET; + continue; + } + + kbd_inter_running = TRUE; + } + + if (kbd_inter_running) { + method = AUTH_KEYBOARD_INTERACTIVE; + type = AUTH_TYPE_KEYBOARD_INTERACTIVE; + tried_keyb_inter = TRUE; + + /* We've got packet with that "interactive" info + dump banners, and set its prompt as ours */ + { + char *name, *inst, *lang, *prompt; + int name_len, inst_len, lang_len, prompt_len; + ssh2_pkt_getstring(&name, &name_len); + ssh2_pkt_getstring(&inst, &inst_len); + ssh2_pkt_getstring(&lang, &lang_len); + if (name_len > 0) + c_write_untrusted(name, name_len); + if (inst_len > 0) + c_write_untrusted(inst, inst_len); + num_prompts = ssh2_pkt_getuint32(); + + ssh2_pkt_getstring(&prompt, &prompt_len); + strncpy(pwprompt, prompt, sizeof(pwprompt)); + need_pw = TRUE; + + echo = ssh2_pkt_getbool(); + } + } + if (!method && can_passwd) { method = AUTH_PASSWORD; sprintf(pwprompt, "%.90s@%.90s's password: ", username, @@ -4331,7 +4418,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"); @@ -4419,7 +4507,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"); @@ -4457,8 +4547,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"); @@ -4500,7 +4590,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"); @@ -4557,7 +4648,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; } /* @@ -4588,7 +4680,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) /* * Transfer data! */ - ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ ssh_send_ok = 1; while (1) { static int try_send; @@ -4716,22 +4808,29 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) c = find234(ssh_channels, &i, ssh_channelfind); if (!c) continue; /* nonexistent channel */ - if (c->closes == 0) { - ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); - ssh2_pkt_adduint32(c->remoteid); - ssh2_pkt_send(); - } /* Do pre-close processing on the channel. */ switch (c->type) { case CHAN_MAINSESSION: break; /* nothing to see here, move along */ case CHAN_X11: + if (c->u.x11.s != NULL) + x11_close(c->u.x11.s); + sshfwd_close(c); break; case CHAN_AGENT: + sshfwd_close(c); break; case CHAN_SOCKDATA: + if (c->u.pfd.s != NULL) + pfd_close(c->u.pfd.s); + sshfwd_close(c); break; } + if (c->closes == 0) { + ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE); + ssh2_pkt_adduint32(c->remoteid); + ssh2_pkt_send(); + } del234(ssh_channels, c); bufchain_clear(&c->v.v2.outbuffer); sfree(c); @@ -4773,6 +4872,21 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) c->v.v2.remmaxpkt = ssh2_pkt_getuint32(); bufchain_init(&c->v.v2.outbuffer); pfd_confirm(c->u.pfd.s); + } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) { + unsigned i = ssh2_pkt_getuint32(); + struct ssh_channel *c; + c = find234(ssh_channels, &i, ssh_channelfind); + if (!c) + continue; /* nonexistent channel */ + if (c->type != CHAN_SOCKDATA_DORMANT) + continue; /* dunno why they're failing this */ + + logevent("Forwarded connection refused by server"); + + pfd_close(c->u.pfd.s); + + del234(ssh_channels, c); + sfree(c); } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) { char *type; int typelen; @@ -5080,7 +5194,7 @@ void *new_sock_channel(Socket s) void ssh_unthrottle(int bufsize) { if (ssh_version == 1) { - if (bufsize < SSH1_BUFFER_LIMIT) { + if (ssh1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { ssh1_stdout_throttling = 0; ssh1_throttle(-1); }