X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/94cd7c3a9121a0a77d0d682f6affb9c00bf695b5..62ddb51e0424dd4bd1098b024f2427959aefc729:/ssh.c diff --git a/ssh.c b/ssh.c index 3d23aa09..953c69f1 100644 --- a/ssh.c +++ b/ssh.c @@ -1,7 +1,12 @@ +/* + * SSH backend. + */ + #include #include #include #include +#include #include "putty.h" #include "tree234.h" @@ -708,7 +713,7 @@ struct ssh_tag { void *cs_comp_ctx, *sc_comp_ctx; const struct ssh_kex *kex; const struct ssh_signkey *hostkey; - unsigned char v2_session_id[32]; + unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN]; int v2_session_id_len; void *kex_ctx; @@ -724,6 +729,7 @@ struct ssh_tag { tree234 *channels; /* indexed by local id */ struct ssh_channel *mainchan; /* primary session channel */ + int ncmode; /* is primary channel direct-tcpip? */ int exitcode; int close_expected; int clean_exit; @@ -1046,7 +1052,7 @@ static void c_write_stderr(int trusted, const char *buf, int len) { int i; for (i = 0; i < len; i++) - if (buf[i] != '\r' && (trusted || buf[i] & 0x60)) + if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60))) fputc(buf[i], stderr); } @@ -1436,11 +1442,17 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p) return biglen + 4; /* len(length+padding+type+data+CRC) */ } +static int s_write(Ssh ssh, void *data, int len) +{ + log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL); + return sk_write(ssh->s, (char *)data, len); +} + static void s_wrpkt(Ssh ssh, struct Packet *pkt) { int len, backlog, offset; len = s_wrpkt_prepare(ssh, pkt, &offset); - backlog = sk_write(ssh->s, (char *)pkt->data + offset, len); + backlog = s_write(ssh, pkt->data + offset, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); ssh_free_packet(pkt); @@ -1477,6 +1489,7 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap) while ((argtype = va_arg(ap, int)) != PKT_END) { unsigned char *argp, argchar; + char *sargp; unsigned long argint; int arglen; switch (argtype) { @@ -1495,8 +1508,8 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap) ssh_pkt_adddata(pkt, argp, arglen); break; case PKT_STR: - argp = va_arg(ap, unsigned char *); - ssh_pkt_addstring(pkt, argp); + sargp = va_arg(ap, char *); + ssh_pkt_addstring(pkt, sargp); break; case PKT_BIGNUM: bn = va_arg(ap, Bignum); @@ -1642,7 +1655,7 @@ static void ssh_pkt_addstring(struct Packet *pkt, char *data) static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b) { int len = ssh1_bignum_length(b); - unsigned char *data = snewn(len, char); + unsigned char *data = snewn(len, unsigned char); (void) ssh1_write_bignum(data, b); ssh_pkt_adddata(pkt, data, len); sfree(data); @@ -1824,7 +1837,7 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt) return; } len = ssh2_pkt_construct(ssh, pkt); - backlog = sk_write(ssh->s, (char *)pkt->data, len); + backlog = s_write(ssh, pkt->data, len); if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog); @@ -1892,7 +1905,6 @@ static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt) ssh2_pkt_send_noqueue(ssh, pkt); } -#if 0 /* disused */ /* * Either queue or defer a packet, depending on whether queueing is * set. @@ -1904,7 +1916,6 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) else ssh2_pkt_defer_noqueue(ssh, pkt, FALSE); } -#endif /* * Send the whole deferred data block constructed by @@ -1922,8 +1933,7 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt) static void ssh_pkt_defersend(Ssh ssh) { int backlog; - backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data, - ssh->deferred_len); + backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len); ssh->deferred_len = ssh->deferred_size = 0; sfree(ssh->deferred_send_data); ssh->deferred_send_data = NULL; @@ -1939,6 +1949,74 @@ static void ssh_pkt_defersend(Ssh ssh) } /* + * Send a packet whose length needs to be disguised (typically + * passwords or keyboard-interactive responses). + */ +static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt, + int padsize) +{ +#if 0 + if (0) { + /* + * The simplest way to do this is to adjust the + * variable-length padding field in the outgoing packet. + * + * Currently compiled out, because some Cisco SSH servers + * don't like excessively padded packets (bah, why's it + * always Cisco?) + */ + pkt->forcepad = padsize; + ssh2_pkt_send(ssh, pkt); + } else +#endif + { + /* + * If we can't do that, however, an alternative approach is + * to use the pkt_defer mechanism to bundle the packet + * tightly together with an SSH_MSG_IGNORE such that their + * combined length is a constant. So first we construct the + * final form of this packet and defer its sending. + */ + ssh2_pkt_defer(ssh, pkt); + + /* + * Now construct an SSH_MSG_IGNORE which includes a string + * that's an exact multiple of the cipher block size. (If + * the cipher is NULL so that the block size is + * unavailable, we don't do this trick at all, because we + * gain nothing by it.) + */ + if (ssh->cscipher) { + int stringlen, i; + + stringlen = (256 - ssh->deferred_len); + stringlen += ssh->cscipher->blksize - 1; + stringlen -= (stringlen % ssh->cscipher->blksize); + if (ssh->cscomp) { + /* + * Temporarily disable actual compression, so we + * can guarantee to get this string exactly the + * length we want it. The compression-disabling + * routine should return an integer indicating how + * many bytes we should adjust our string length + * by. + */ + stringlen -= + ssh->cscomp->disable_compression(ssh->cs_comp_ctx); + } + pkt = ssh2_pkt_init(SSH2_MSG_IGNORE); + ssh2_pkt_addstring_start(pkt); + for (i = 0; i < stringlen; i++) { + char c = (char) random_byte(); + ssh2_pkt_addstring_data(pkt, &c, 1); + } + ssh2_pkt_defer(ssh, pkt); + } + ssh_pkt_defersend(ssh); + } +} + +/* * Send all queued SSH-2 packets. We send them by means of * ssh2_pkt_defer_noqueue(), in case they included a pair of * packets that needed to be lumped together. @@ -2155,6 +2233,13 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring) ssh->remote_bugs = 0; + /* + * General notes on server version strings: + * - Not all servers reporting "Cisco-1.25" have all the bugs listed + * here -- in particular, we've heard of one that's perfectly happy + * with SSH1_MSG_IGNOREs -- but this string never seems to change, + * so we can't distinguish them. + */ if (ssh->cfg.sshbug_ignore1 == FORCE_ON || (ssh->cfg.sshbug_ignore1 == AUTO && (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") || @@ -2412,7 +2497,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c) } logeventf(ssh, "We claim version: %.*s", strcspn(verstring, "\015\012"), verstring); - sk_write(ssh->s, verstring, strlen(verstring)); + s_write(ssh, verstring, strlen(verstring)); sfree(verstring); if (ssh->version == 2) do_ssh2_transport(ssh, NULL, -1, NULL); @@ -2432,7 +2517,9 @@ static int do_ssh_init(Ssh ssh, unsigned char c) static void ssh_process_incoming_data(Ssh ssh, unsigned char **data, int *datalen) { - struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen); + struct Packet *pktin; + + pktin = ssh->s_rdpkt(ssh, data, datalen); if (pktin) { ssh->protocol(ssh, NULL, 0, pktin); ssh_free_packet(pktin); @@ -2475,6 +2562,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen) static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen) { + /* Log raw data, if we're in that mode. */ + log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL); + crBegin(ssh->ssh_gotdata_crstate); /* @@ -3222,7 +3312,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, while (pktin->type == SSH1_SMSG_FAILURE) { s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; - if (agent_exists() && !s->tried_agent) { + if (ssh->cfg.tryagent && agent_exists() && !s->tried_agent) { /* * Attempt RSA authentication using Pageant. */ @@ -3687,19 +3777,19 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, * magnitude of the password length, but it will * introduce a bit of extra uncertainty. * - * A few servers (the old 1.2.18 through 1.2.22) - * can't deal with SSH1_MSG_IGNORE. For these - * servers, we need an alternative defence. We make - * use of the fact that the password is interpreted - * as a C string: so we can append a NUL, then some - * random data. + * A few servers can't deal with SSH1_MSG_IGNORE, at + * least in this context. For these servers, we need + * an alternative defence. We make 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 + * A few servers can deal with neither SSH1_MSG_IGNORE + * here _nor_ a padded password string. + * For these servers we are left with no defences * against password length sniffing. */ - if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { + if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) && + !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) { /* * The server can deal with SSH1_MSG_IGNORE, so * we can use the primary defence. @@ -3768,10 +3858,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, PKTT_OTHER, 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. + * The server is believed unable to cope with + * any of our password camouflage methods. */ int len; len = strlen(s->cur_prompt->prompts[0]->result); @@ -4553,7 +4641,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) /* Data for an agent message. Buffer it. */ while (len > 0) { if (c->u.a.lensofar < 4) { - unsigned int l = min(4 - c->u.a.lensofar, len); + unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len); memcpy(c->u.a.msglen + c->u.a.lensofar, p, l); p += l; @@ -4570,7 +4658,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar >= 4 && len > 0) { unsigned int l = min(c->u.a.totallen - c->u.a.lensofar, - len); + (unsigned)len); memcpy(c->u.a.message + c->u.a.lensofar, p, l); p += l; @@ -4964,7 +5052,10 @@ static int first_in_commasep_string(char *needle, char *haystack, int haylen) /* * SSH-2 key creation method. + * (Currently assumes 2 lots of any hash are sufficient to generate + * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.) */ +#define SSH2_MKKEY_ITERS (2) static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr, unsigned char *keyspace) { @@ -5011,7 +5102,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, char *hostkeydata, *sigdata, *keystr, *fingerprint; int hostkeylen, siglen; void *hkey; /* actual host key */ - unsigned char exchange_hash[32]; + unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN]; int n_preferred_kex; const struct ssh_kexes *preferred_kex[KEX_MAX]; int n_preferred_ciphers; @@ -5064,7 +5155,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, s->preferred_kex[s->n_preferred_kex++] = &ssh_diffiehellman_group1; break; - case CIPHER_WARN: + case KEX_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ if (i < KEX_MAX - 1) { @@ -5519,7 +5610,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, ssh->kex->groupname); } - logevent("Doing Diffie-Hellman key exchange"); + logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s", + ssh->kex->hash->text_name); /* * Now generate and send e for Diffie-Hellman. */ @@ -5661,13 +5753,21 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * hash from the _first_ key exchange. */ { - unsigned char keyspace[40]; + unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS]; + assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace); + assert((ssh->cscipher->keylen+7) / 8 <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace); + assert(ssh->cscipher->blksize <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace); + assert(ssh->csmac->len <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace); + memset(keyspace, 0, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s client->server encryption", @@ -5719,13 +5819,21 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen, * hash from the _first_ key exchange. */ { - unsigned char keyspace[40]; + unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS]; + assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace); + assert((ssh->sccipher->keylen+7) / 8 <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace); + assert(ssh->sccipher->blksize <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace); ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace); + assert(ssh->scmac->len <= + ssh->kex->hash->hlen * SSH2_MKKEY_ITERS); ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace); + memset(keyspace, 0, sizeof(keyspace)); } logeventf(ssh, "Initialised %.200s server->client encryption", ssh->sccipher->text_name); @@ -5970,7 +6078,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) case CHAN_AGENT: while (length > 0) { if (c->u.a.lensofar < 4) { - unsigned int l = min(4 - c->u.a.lensofar, length); + unsigned int l = min(4 - c->u.a.lensofar, + (unsigned)length); memcpy(c->u.a.msglen + c->u.a.lensofar, data, l); data += l; @@ -5987,7 +6096,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin) if (c->u.a.lensofar >= 4 && length > 0) { unsigned int l = min(c->u.a.totallen - c->u.a.lensofar, - length); + (unsigned)length); memcpy(c->u.a.message + c->u.a.lensofar, data, l); data += l; @@ -6613,7 +6722,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->nkeys = 0; s->agent_response = NULL; s->pkblob_in_agent = NULL; - if (agent_exists()) { + if (ssh->cfg.tryagent && agent_exists()) { void *r; @@ -7332,7 +7441,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Send the responses to the server. */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE); - s->pktout->forcepad = 256; ssh2_pkt_adduint32(s->pktout, s->num_prompts); for (i=0; i < s->num_prompts; i++) { dont_log_password(ssh, s->pktout, PKTLOG_BLANK); @@ -7340,7 +7448,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->prompts[i]->result); end_log_omission(ssh, s->pktout); } - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); /* * Get the next packet in case it's another @@ -7410,7 +7518,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * people who find out how long their password is! */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - s->pktout->forcepad = 256; ssh2_pkt_addstring(s->pktout, s->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ @@ -7419,7 +7526,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, dont_log_password(ssh, s->pktout, PKTLOG_BLANK); ssh2_pkt_addstring(s->pktout, s->password); end_log_omission(ssh, s->pktout); - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent password"); s->type = AUTH_TYPE_PASSWORD; @@ -7461,6 +7568,21 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, s->cur_prompt->instruction = dupprintf("%.*s", prompt_len, prompt); s->cur_prompt->instr_reqd = TRUE; + /* + * There's no explicit requirement in the protocol + * for the "old" passwords in the original and + * password-change messages to be the same, and + * apparently some Cisco kit supports password change + * by the user entering a blank password originally + * and the real password subsequently, so, + * reluctantly, we prompt for the old password again. + * + * (On the other hand, some servers don't even bother + * to check this field.) + */ + add_prompt(s->cur_prompt, + dupstr("Current password (blank for previously entered password): "), + FALSE, SSH_MAX_PASSWORD_LEN); add_prompt(s->cur_prompt, dupstr("Enter new password: "), FALSE, SSH_MAX_PASSWORD_LEN); add_prompt(s->cur_prompt, dupstr("Confirm new password: "), @@ -7494,10 +7616,25 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, } /* - * Check the two passwords match. + * If the user specified a new original password + * (IYSWIM), overwrite any previously specified + * one. + * (A side effect is that the user doesn't have to + * re-enter it if they louse up the new password.) + */ + if (s->cur_prompt->prompts[0]->result[0]) { + memset(s->password, 0, strlen(s->password)); + /* burn the evidence */ + sfree(s->password); + s->password = + dupstr(s->cur_prompt->prompts[0]->result); + } + + /* + * Check the two new passwords match. */ - got_new = (strcmp(s->cur_prompt->prompts[0]->result, - s->cur_prompt->prompts[1]->result) + got_new = (strcmp(s->cur_prompt->prompts[1]->result, + s->cur_prompt->prompts[2]->result) == 0); if (!got_new) /* They don't. Silly user. */ @@ -7510,7 +7647,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * (see above for padding rationale) */ s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST); - s->pktout->forcepad = 256; ssh2_pkt_addstring(s->pktout, s->username); ssh2_pkt_addstring(s->pktout, "ssh-connection"); /* service requested */ @@ -7519,10 +7655,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, dont_log_password(ssh, s->pktout, PKTLOG_BLANK); ssh2_pkt_addstring(s->pktout, s->password); ssh2_pkt_addstring(s->pktout, - s->cur_prompt->prompts[0]->result); + s->cur_prompt->prompts[1]->result); free_prompts(s->cur_prompt); end_log_omission(ssh, s->pktout); - ssh2_pkt_send(ssh, s->pktout); + ssh2_pkt_send_with_padding(ssh, s->pktout, 256); logevent("Sent new password"); /* @@ -7597,7 +7733,58 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Create the main session channel. */ - if (!ssh->cfg.ssh_no_shell) { + if (ssh->cfg.ssh_no_shell) { + ssh->mainchan = NULL; + } else if (*ssh->cfg.ssh_nc_host) { + /* + * Just start a direct-tcpip channel and use it as the main + * channel. + */ + ssh->mainchan = snew(struct ssh_channel); + ssh->mainchan->ssh = ssh; + ssh->mainchan->localid = alloc_channel_id(ssh); + logeventf(ssh, + "Opening direct-tcpip channel to %s:%d in place of session", + ssh->cfg.ssh_nc_host, ssh->cfg.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); + ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE; + 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, ssh->cfg.ssh_nc_host); + ssh2_pkt_adduint32(s->pktout, ssh->cfg.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; + } + ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin); + ssh->mainchan->halfopen = FALSE; + ssh->mainchan->type = CHAN_MAINSESSION; + ssh->mainchan->closes = 0; + ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin); + ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin); + bufchain_init(&ssh->mainchan->v.v2.outbuffer); + 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; ssh->mainchan->localid = alloc_channel_id(ssh); @@ -7628,8 +7815,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, add234(ssh->channels, ssh->mainchan); update_specials_menu(ssh->frontend); logevent("Opened channel for session"); - } else - ssh->mainchan = NULL; + ssh->ncmode = FALSE; + } /* * Now we have a channel, make dispatch table entries for @@ -7652,7 +7839,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable X11 forwarding. */ - if (ssh->mainchan && ssh->cfg.x11_forward) { + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) { char proto[20], data[64]; logevent("Requesting X11 forwarding"); ssh->x11auth = x11_invent_auth(proto, sizeof(proto), @@ -7700,7 +7887,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Potentially enable agent forwarding. */ - if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) { + if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) { logevent("Requesting OpenSSH-style agent forwarding"); s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST); ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid); @@ -7726,7 +7913,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, /* * Now allocate a pty for the session. */ - if (ssh->mainchan && !ssh->cfg.nopty) { + if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) { /* Unpick the terminal-speed string. */ /* XXX perhaps we should allow no speeds to be sent. */ ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */ @@ -7776,7 +7963,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * Simplest thing here is to send all the requests at once, and * then wait for a whole bunch of successes or failures. */ - if (ssh->mainchan && *ssh->cfg.environmt) { + if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) { char *e = ssh->cfg.environmt; char *var, *varend, *val; @@ -7841,7 +8028,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, * this twice if the config data has provided a second choice * of command. */ - if (ssh->mainchan) while (1) { + if (ssh->mainchan && !ssh->ncmode) while (1) { int subsys; char *cmd; @@ -8690,17 +8877,20 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org) * too much hassle to keep track, and partly I'm not * convinced the server should be told details like that * about my local network configuration. + * The "originator IP address" is syntactically a numeric + * IP address, and some servers (e.g., Tectia) get upset + * if it doesn't match this syntax. */ - ssh2_pkt_addstring(pktout, "client-side-connection"); + ssh2_pkt_addstring(pktout, "0.0.0.0"); ssh2_pkt_adduint32(pktout, 0); ssh2_pkt_send(ssh, pktout); } } -static Socket ssh_socket(void *handle) +static int ssh_connected(void *handle) { Ssh ssh = (Ssh) handle; - return ssh->s; + return ssh->s != NULL; } static int ssh_sendok(void *handle) @@ -8737,7 +8927,7 @@ static int ssh_return_exitcode(void *handle) if (ssh->s != NULL) return -1; else - return (ssh->exitcode >= 0 ? ssh->exitcode : 0); + return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX); } /* @@ -8770,7 +8960,7 @@ Backend ssh_backend = { ssh_size, ssh_special, ssh_get_specials, - ssh_socket, + ssh_connected, ssh_return_exitcode, ssh_sendok, ssh_ldisc,