X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/e14d0f19126c0fb1cf9ee3cdb7d69f9f3d42c035..babac7bd70fda1ec0c9810c8f4ccf08cfa1e31fb:/ssh.c diff --git a/ssh.c b/ssh.c index 27b7216d..9f27e3a9 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) | \ @@ -233,18 +235,6 @@ extern void pfd_override_throttle(Socket s, int enable); #define SSH_MAX_BACKLOG 32768 #define OUR_V2_WINSIZE 16384 -/* - * Ciphers for SSH2. We miss out single-DES because it isn't - * supported; also 3DES and Blowfish are both done differently from - * SSH1. (3DES uses outer chaining; Blowfish has the opposite - * endianness and different-sized keys.) - */ -const static struct ssh2_ciphers *ciphers[] = { - &ssh2_aes, - &ssh2_blowfish, - &ssh2_3des, -}; - const static struct ssh_kex *kex_algs[] = { &ssh_diffiehellman_gex, &ssh_diffiehellman @@ -607,12 +597,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; @@ -770,11 +754,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); @@ -950,23 +930,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 */ @@ -1249,13 +1224,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; @@ -1428,17 +1405,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 @@ -1448,6 +1423,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)) { @@ -1489,8 +1474,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; @@ -1516,10 +1501,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); /* @@ -1662,7 +1647,7 @@ static void ssh_sent(Plug plug, int bufsize) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static char *connect_to_host(char *host, int port, char **realhost) +static char *connect_to_host(char *host, int port, char **realhost, int nodelay) { static struct plug_function_table fn_table = { ssh_closing, @@ -1719,7 +1704,7 @@ static char *connect_to_host(char *host, int port, char **realhost) sprintf(buf, "Connecting to %.100s port %d", addrbuf, port); logevent(buf); } - s = sk_new(addr, port, 0, 1, &fn_table_ptr); + s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr); if ((err = sk_socket_error(s))) return err; @@ -2415,26 +2400,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; @@ -2466,7 +2442,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); @@ -2497,19 +2511,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"); } @@ -2735,7 +2759,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) { @@ -2876,9 +2900,21 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) if (c && c->type == CHAN_SOCKDATA_DORMANT) { c->remoteid = localid; c->type = CHAN_SOCKDATA; + c->v.v1.throttling = 0; 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); @@ -3005,6 +3041,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; @@ -3098,6 +3143,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) static int n_preferred_ciphers; static const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX]; static const struct ssh_compress *preferred_comp; + static int cipherstr_started; static int first_kex; crBegin; @@ -3115,7 +3161,10 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) n_preferred_ciphers++; break; case CIPHER_DES: - /* Not supported in SSH2; silently drop */ + if (cfg.ssh2_des_cbc) { + preferred_ciphers[n_preferred_ciphers] = &ssh2_des; + n_preferred_ciphers++; + } break; case CIPHER_3DES: preferred_ciphers[n_preferred_ciphers] = &ssh2_3des; @@ -3175,24 +3224,28 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) } /* List client->server encryption algorithms. */ ssh2_pkt_addstring_start(); + cipherstr_started = 0; for (i = 0; i < n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = preferred_ciphers[i]; if (!c) continue; /* warning flag */ for (j = 0; j < c->nciphers; j++) { - ssh2_pkt_addstring_str(c->list[j]->name); - if (i < n_preferred_ciphers || j < c->nciphers - 1) + if (cipherstr_started) ssh2_pkt_addstring_str(","); + ssh2_pkt_addstring_str(c->list[j]->name); + cipherstr_started = 1; } } /* List server->client encryption algorithms. */ ssh2_pkt_addstring_start(); + cipherstr_started = 0; for (i = 0; i < n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = preferred_ciphers[i]; if (!c) continue; /* warning flag */ for (j = 0; j < c->nciphers; j++) { - ssh2_pkt_addstring_str(c->list[j]->name); - if (i < n_preferred_ciphers || j < c->nciphers - 1) + if (cipherstr_started) ssh2_pkt_addstring_str(","); + ssh2_pkt_addstring_str(c->list[j]->name); + cipherstr_started = 1; } } /* List client->server MAC algorithms. */ @@ -3296,6 +3349,11 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) break; } } + if (!cscipher_tobe) { + bombout(("Couldn't agree a client-to-server cipher (available: %s)", str)); + crReturn(0); + } + ssh2_pkt_getstring(&str, &len); /* server->client cipher */ warn = 0; for (i = 0; i < n_preferred_ciphers; i++) { @@ -3316,6 +3374,11 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) break; } } + if (!sccipher_tobe) { + bombout(("Couldn't agree a server-to-client cipher (available: %s)", str)); + crReturn(0); + } + ssh2_pkt_getstring(&str, &len); /* client->server mac */ for (i = 0; i < nmacs; i++) { if (in_commasep_string(maclist[i]->name, str, len)) { @@ -3443,15 +3506,6 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) } /* - * Expect SSH2_MSG_NEWKEYS from server. - */ - crWaitUntil(ispkt); - if (pktin.type != SSH2_MSG_NEWKEYS) { - bombout(("expected new-keys packet from server")); - crReturn(0); - } - - /* * Authenticate remote host: verify host key. (We've already * checked the signature of the exchange hash.) */ @@ -3474,6 +3528,15 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt) ssh2_pkt_send(); /* + * Expect SSH2_MSG_NEWKEYS from server. + */ + crWaitUntil(ispkt); + if (pktin.type != SSH2_MSG_NEWKEYS) { + bombout(("expected new-keys packet from server")); + crReturn(0); + } + + /* * Create and initialise session keys. */ cscipher = cscipher_tobe; @@ -3649,6 +3712,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) * the username they will want to be able to get back and * retype it! */ + username[0] = '\0'; do { static int pos; static char c; @@ -3657,7 +3721,13 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) * Get a username. */ pos = 0; - if ((flags & FLAG_INTERACTIVE) && !*cfg.username) { + if (*username && !cfg.change_username) { + /* + * We got a username last time round this loop, and + * with change_username turned off we don't try to get + * it again. + */ + } else if ((flags & FLAG_INTERACTIVE) && !*cfg.username) { if (ssh_get_line) { if (!ssh_get_line("login as: ", username, sizeof(username), FALSE)) { @@ -3844,7 +3914,7 @@ 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_keyb_inter = + can_keyb_inter = cfg.try_ki_auth && in_commasep_string("keyboard-interactive", methods, methlen); } @@ -4089,6 +4159,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) ssh2_pkt_getstring(&prompt, &prompt_len); strncpy(pwprompt, prompt, sizeof(pwprompt)); + pwprompt[prompt_len < sizeof(pwprompt) ? + prompt_len : sizeof(pwprompt)-1] = '\0'; need_pw = TRUE; echo = ssh2_pkt_getbool(); @@ -4380,7 +4452,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"); @@ -4468,7 +4541,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"); @@ -4506,8 +4581,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"); @@ -4549,7 +4624,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"); @@ -4606,7 +4682,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; } /* @@ -4637,7 +4714,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; @@ -4735,8 +4812,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) ssh_state = SSH_STATE_CLOSED; logevent("Received disconnect message"); crReturnV; - } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) { - continue; /* exit status et al; ignore (FIXME?) */ } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) { unsigned i = ssh2_pkt_getuint32(); struct ssh_channel *c; @@ -4796,12 +4871,25 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) * See if that was the last channel left open. */ if (count234(ssh_channels) == 0) { +#if 0 + /* + * We used to send SSH_MSG_DISCONNECT here, + * because I'd believed that _every_ conforming + * SSH2 connection had to end with a disconnect + * being sent by at least one side; apparently + * I was wrong and it's perfectly OK to + * unceremoniously slam the connection shut + * when you're done, and indeed OpenSSH feels + * this is more polite than sending a + * DISCONNECT. So now we don't. + */ logevent("All channels closed. Disconnecting"); ssh2_pkt_init(SSH2_MSG_DISCONNECT); ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION); ssh2_pkt_addstring("All open channels closed"); ssh2_pkt_addstring("en"); /* language tag */ ssh2_pkt_send(); +#endif ssh_state = SSH_STATE_CLOSED; crReturnV; } @@ -4824,11 +4912,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; @@ -4844,6 +4943,46 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt) del234(ssh_channels, c); sfree(c); + } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) { + unsigned localid; + char *type; + int typelen, want_reply; + struct ssh_channel *c; + + localid = ssh2_pkt_getuint32(); + ssh2_pkt_getstring(&type, &typelen); + want_reply = ssh2_pkt_getbool(); + + /* + * First, check that the channel exists. Otherwise, + * we can instantly disconnect with a rude message. + */ + c = find234(ssh_channels, &localid, ssh_channelfind); + if (!c) { + char buf[80]; + sprintf(buf, "Received channel request for nonexistent" + " channel %d", localid); + logevent(buf); + ssh2_pkt_init(SSH2_MSG_DISCONNECT); + ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION); + ssh2_pkt_addstring(buf); + ssh2_pkt_addstring("en"); /* language tag */ + ssh2_pkt_send(); + connection_fatal(buf); + ssh_state = SSH_STATE_CLOSED; + crReturnV; + } + + /* + * We don't recognise any form of channel request, + * so we now either ignore the request or respond + * with CHANNEL_FAILURE, depending on want_reply. + */ + if (want_reply) { + ssh2_pkt_init(SSH2_MSG_CHANNEL_FAILURE); + ssh2_pkt_adduint32(c->remoteid); + ssh2_pkt_send(); + } } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) { char *type; int typelen; @@ -4987,7 +5126,7 @@ 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) +static char *ssh_init(char *host, int port, char **realhost, int nodelay) { char *p; @@ -5003,7 +5142,7 @@ static char *ssh_init(char *host, int port, char **realhost) ssh_overall_bufsize = 0; ssh_fallback_cmd = 0; - p = connect_to_host(host, port, realhost); + p = connect_to_host(host, port, realhost, nodelay); if (p != NULL) return p;