#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
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 */
/*
* 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;
* 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,
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;
* against password length sniffing.
*/
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;
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;
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");
}
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);
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;
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;
}
/* 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. */
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++) {
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)) {
crReturn(0);
}
- /*
- * 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.)
ssh2_pkt_init(SSH2_MSG_NEWKEYS);
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.
*/
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);
}
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();
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;
* 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;
}
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;
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;
*
* 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;
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;