#define logevent(s) { logevent(s); \
if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
- fprintf(stderr, "%s\n", s); }
+ { fprintf(stderr, "%s\n", s); fflush(stderr); } }
#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
(s ? sk_close(s), s = NULL : 0), \
#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 /* 0x9 */
#define SSH2_DISCONNECT_CONNECTION_LOST 10 /* 0xa */
#define SSH2_DISCONNECT_BY_APPLICATION 11 /* 0xb */
+#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 /* 0xc */
+#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 /* 0xd */
+#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 /* 0xe */
+#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* 0xf */
+
+static const char *const ssh2_disconnect_reasons[] = {
+ NULL,
+ "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
+ "SSH_DISCONNECT_PROTOCOL_ERROR",
+ "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
+ "SSH_DISCONNECT_HOST_AUTHENTICATION_FAILED",
+ "SSH_DISCONNECT_MAC_ERROR",
+ "SSH_DISCONNECT_COMPRESSION_ERROR",
+ "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
+ "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
+ "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
+ "SSH_DISCONNECT_CONNECTION_LOST",
+ "SSH_DISCONNECT_BY_APPLICATION",
+ "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
+ "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
+ "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
+ "SSH_DISCONNECT_ILLEGAL_USER_NAME",
+};
#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */
#define SSH2_OPEN_CONNECT_FAILED 2 /* 0x2 */
return 0;
}
+static int alloc_channel_id(void) {
+ const unsigned CHANNEL_NUMBER_OFFSET = 256;
+ unsigned low, high, mid;
+ int tsize;
+ struct ssh_channel *c;
+
+ /*
+ * First-fit allocation of channel numbers: always pick the
+ * lowest unused one. To do this, binary-search using the
+ * counted B-tree to find the largest channel ID which is in a
+ * contiguous sequence from the beginning. (Precisely
+ * everything in that sequence must have ID equal to its tree
+ * index plus CHANNEL_NUMBER_OFFSET.)
+ */
+ tsize = count234(ssh_channels);
+
+ low = -1; high = tsize;
+ while (high - low > 1) {
+ mid = (high + low) / 2;
+ c = index234(ssh_channels, mid);
+ if (c->localid == mid + CHANNEL_NUMBER_OFFSET)
+ low = mid; /* this one is fine */
+ else
+ high = mid; /* this one is past it */
+ }
+ /*
+ * Now low points to either -1, or the tree index of the
+ * largest ID in the initial sequence.
+ */
+ {
+ unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;
+ assert(NULL == find234(ssh_channels, &i, ssh_channelfind));
+ }
+ return low + 1 + CHANNEL_NUMBER_OFFSET;
+}
+
static void c_write (char *buf, int len) {
if ((flags & FLAG_STDERR)) {
int i;
goto next_packet;
}
+ if (pktin.type == SSH1_MSG_DISCONNECT) {
+ /* log reason code in disconnect message */
+ char buf[256];
+ unsigned msglen = GET_32BIT(pktin.body);
+ unsigned nowlen;
+ strcpy(buf, "Remote sent disconnect: ");
+ nowlen = strlen(buf);
+ if (msglen > sizeof(buf)-nowlen-1)
+ msglen = sizeof(buf)-nowlen-1;
+ memcpy(buf+nowlen, pktin.body+4, msglen);
+ buf[nowlen+msglen] = '\0';
+ logevent(buf);
+ }
+
crFinish(0);
}
if (pktin.type == SSH2_MSG_IGNORE || pktin.type == SSH2_MSG_DEBUG)
goto next_packet; /* FIXME: print DEBUG message */
+ if (pktin.type == SSH2_MSG_DISCONNECT) {
+ /* log reason code in disconnect message */
+ char buf[256];
+ int reason = GET_32BIT(pktin.data+6);
+ unsigned msglen = GET_32BIT(pktin.data+10);
+ unsigned nowlen;
+ if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
+ sprintf(buf, "Received disconnect message (%s)",
+ ssh2_disconnect_reasons[reason]);
+ } else {
+ sprintf(buf, "Received disconnect message (unknown type %d)", reason);
+ }
+ logevent(buf);
+ strcpy(buf, "Disconnection message text: ");
+ nowlen = strlen(buf);
+ if (msglen > sizeof(buf)-nowlen-1)
+ msglen = sizeof(buf)-nowlen-1;
+ memcpy(buf+nowlen, pktin.data+14, msglen);
+ buf[nowlen+msglen] = '\0';
+ logevent(buf);
+ }
+
crFinish(0);
}
}
static char *ssh2_mpint_fmt(Bignum b, int *len) {
unsigned char *p;
- int i, n = (ssh1_bignum_bitcount(b)+7)/8;
+ int i, n = (bignum_bitcount(b)+7)/8;
p = smalloc(n + 1);
if (!p)
fatalbox("out of memory");
char *imp; /* pointer to implementation part */
imp = vstring;
imp += strcspn(imp, "-");
+ if (*imp) imp++;
imp += strcspn(imp, "-");
+ if (*imp) imp++;
ssh_remote_bugs = 0;
}
static int do_ssh_init(unsigned char c) {
- static char *vsp;
+ static char vslen;
static char version[10];
- static char vstring[80];
- static char vlog[sizeof(vstring)+20];
+ static char *vstring;
+ static int vstrsize;
+ static char *vlog;
static int i;
crBegin;
crReturn(1); /* get another character */
}
+ vstring = smalloc(16);
+ vstrsize = 16;
strcpy(vstring, "SSH-");
- vsp = vstring+4;
+ vslen = 4;
i = 0;
while (1) {
crReturn(1); /* get another char */
- if (vsp < vstring+sizeof(vstring)-1)
- *vsp++ = c;
+ if (vslen >= vstrsize-1) {
+ vstrsize += 16;
+ vstring = srealloc(vstring, vstrsize);
+ }
+ vstring[vslen++] = c;
if (i >= 0) {
if (c == '-') {
version[i] = '\0';
ssh_agentfwd_enabled = FALSE;
rdpkt2_state.incoming_sequence = 0;
- *vsp = 0;
+ vstring[vslen] = 0;
+ vlog = smalloc(20 + vslen);
sprintf(vlog, "Server version: %s", vstring);
ssh_detect_bugs(vstring);
vlog[strcspn(vlog, "\r\n")] = '\0';
logevent(vlog);
+ sfree(vlog);
/*
* Server version "1.99" means we can choose whether we use v1
/*
* This is a v2 server. Begin v2 protocol.
*/
- char verstring[80];
+ char verstring[80], vlog[100];
sprintf(verstring, "SSH-2.0-%s", sshver);
SHA_Init(&exhashbase);
/*
*/
sha_string(&exhashbase, verstring, strlen(verstring));
sha_string(&exhashbase, vstring, strcspn(vstring, "\r\n"));
- sprintf(vstring, "%s\n", verstring);
sprintf(vlog, "We claim version: %s", verstring);
logevent(vlog);
+ strcat(verstring, "\n");
logevent("Using SSH protocol version 2");
- sk_write(s, vstring, strlen(vstring));
+ sk_write(s, verstring, strlen(verstring));
ssh_protocol = ssh2_protocol;
ssh_version = 2;
s_rdpkt = ssh2_rdpkt;
/*
* This is a v1 server. Begin v1 protocol.
*/
- sprintf(vstring, "SSH-%s-%s\n",
+ char verstring[80], vlog[100];
+ sprintf(verstring, "SSH-%s-%s",
(ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"),
sshver);
- sprintf(vlog, "We claim version: %s", vstring);
- vlog[strcspn(vlog, "\r\n")] = '\0';
+ sprintf(vlog, "We claim version: %s", verstring);
logevent(vlog);
+ strcat(verstring, "\n");
logevent("Using SSH protocol version 1");
- sk_write(s, vstring, strlen(vstring));
+ sk_write(s, verstring, strlen(verstring));
ssh_protocol = ssh1_protocol;
ssh_version = 1;
s_rdpkt = ssh1_rdpkt;
}
ssh_state = SSH_STATE_BEFORE_SIZE;
+ sfree(vstring);
+
crFinish(0);
}
if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
c_write_str("Selected cipher not supported, falling back to 3DES\r\n");
cipher_type = SSH_CIPHER_3DES;
+ if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
+ bombout(("Server violates SSH 1 protocol by "
+ "not supporting 3DES encryption"));
+ crReturn(0);
+ }
}
switch (cipher_type) {
case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break;
PUT_32BIT(agentreq, len);
q = agentreq + 4;
*q++ = SSH1_AGENTC_RSA_CHALLENGE;
- PUT_32BIT(q, ssh1_bignum_bitcount(key.modulus));
+ PUT_32BIT(q, bignum_bitcount(key.modulus));
q += 4;
q += ssh1_write_bignum(q, key.exponent);
q += ssh1_write_bignum(q, key.modulus);
zlib_decompress_init();
}
- if (*cfg.remote_cmd)
- send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd, PKT_END);
+ if (*cfg.remote_cmd_ptr)
+ send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd_ptr, PKT_END);
else
send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
logevent("Started session");
} else if (pktin.type == SSH1_SMSG_X11_OPEN) {
/* Remote side is trying to open a channel to talk to our
* X-Server. Give them back a local channel number. */
- unsigned i;
- struct ssh_channel *c, *d;
- enum234 e;
+ struct ssh_channel *c;
logevent("Received X11 connect request");
/* Refuse if X11 forwarding is disabled. */
PKT_END);
} else {
logevent("opening X11 forward connection succeeded");
- for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) {
- if (d->localid > i)
- break; /* found a free number */
- i = d->localid + 1;
- }
c->remoteid = GET_32BIT(pktin.body);
- c->localid = i;
+ c->localid = alloc_channel_id();
c->closes = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh_channels, c);
} else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
- unsigned i;
struct ssh_channel *c;
- enum234 e;
/* Refuse if agent forwarding is disabled. */
if (!ssh_agentfwd_enabled) {
PKT_INT, GET_32BIT(pktin.body),
PKT_END);
} else {
- i = 1;
- for (c = first234(ssh_channels, &e); c; c = next234(&e)) {
- if (c->localid > i)
- break; /* found a free number */
- i = c->localid + 1;
- }
c = smalloc(sizeof(struct ssh_channel));
c->remoteid = GET_32BIT(pktin.body);
- c->localid = i;
+ c->localid = alloc_channel_id();
c->closes = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
if (c) {
int closetype;
closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
- send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
+ if (!(c->closes & closetype))
+ send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
if ((c->closes == 0) && (c->type == CHAN_X11)) {
logevent("X11 connection closed");
assert(c->u.x11.s != NULL);
while (pktin.type == SSH2_MSG_USERAUTH_BANNER) {
char *banner;
int size;
- ssh2_pkt_getstring(&banner, &size);
- if (banner)
- c_write_untrusted(banner, size);
+ /*
+ * Don't show the banner if we're operating in
+ * non-verbose non-interactive mode. (It's probably
+ * a script, which means nobody will read the
+ * banner _anyway_, and moreover the printing of
+ * the banner will screw up processing on the
+ * output of (say) plink.)
+ */
+ if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
+ ssh2_pkt_getstring(&banner, &size);
+ if (banner)
+ c_write_untrusted(banner, size);
+ }
crWaitUntilV(ispkt);
}
if (pktin.type == SSH2_MSG_USERAUTH_SUCCESS) {
if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
bombout(("Strange packet received during authentication: type %d",
pktin.type));
+ crReturnV;
}
gotit = FALSE;
method = 0;
- if (!method && can_pubkey && agent_exists && !tried_agent) {
+ if (!method && can_pubkey && agent_exists() && !tried_agent) {
/*
* Attempt public-key authentication using Pageant.
*/
continue;
}
- c_write_str("Authenticating with public key \"");
- c_write(commentp, commentlen);
- c_write_str("\" from agent\r\n");
+ if (flags & FLAG_VERBOSE) {
+ c_write_str("Authenticating with public key \"");
+ c_write(commentp, commentlen);
+ c_write_str("\" from agent\r\n");
+ }
/*
* Server is willing to accept the key.
/*
* So now create a channel with a session in it.
*/
+ ssh_channels = newtree234(ssh_channelcmp);
mainchan = smalloc(sizeof(struct ssh_channel));
- mainchan->localid = 100; /* as good as any */
+ mainchan->localid = alloc_channel_id();
ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
ssh2_pkt_addstring("session");
ssh2_pkt_adduint32(mainchan->localid);
mainchan->v2.remmaxpkt = ssh2_pkt_getuint32();
mainchan->v2.outbuffer = NULL;
mainchan->v2.outbuflen = mainchan->v2.outbufsize = 0;
- ssh_channels = newtree234(ssh_channelcmp);
add234(ssh_channels, mainchan);
logevent("Opened channel for session");
if (cfg.ssh_subsys) {
ssh2_pkt_addstring("subsystem");
ssh2_pkt_addbool(1); /* want reply */
- ssh2_pkt_addstring(cfg.remote_cmd);
- } else if (*cfg.remote_cmd) {
+ ssh2_pkt_addstring(cfg.remote_cmd_ptr);
+ } else if (*cfg.remote_cmd_ptr) {
ssh2_pkt_addstring("exec");
ssh2_pkt_addbool(1); /* want reply */
- ssh2_pkt_addstring(cfg.remote_cmd);
+ ssh2_pkt_addstring(cfg.remote_cmd_ptr);
} else {
ssh2_pkt_addstring("shell");
ssh2_pkt_addbool(1); /* want reply */
} else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
unsigned i = ssh2_pkt_getuint32();
struct ssh_channel *c;
- enum234 e;
c = find234(ssh_channels, &i, ssh_channelfind);
if (!c)
/*
* See if that was the last channel left open.
*/
- c = first234(ssh_channels, &e);
- if (!c) {
+ if (count234(ssh_channels) == 0) {
logevent("All channels closed. Disconnecting");
ssh2_pkt_init(SSH2_MSG_DISCONNECT);
ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
c = find234(ssh_channels, &i, ssh_channelfind);
if (!c)
continue; /* nonexistent channel */
- mainchan->v2.remwindow += ssh2_pkt_getuint32();
+ c->v2.remwindow += ssh2_pkt_getuint32();
try_send = TRUE;
} else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
char *type;
ssh2_pkt_send();
sfree(c);
} else {
- struct ssh_channel *d;
- unsigned i;
- enum234 e;
-
- for (i=1, d = first234(ssh_channels, &e); d;
- d = next234(&e)) {
- if (d->localid > i)
- break; /* found a free number */
- i = d->localid + 1;
- }
- c->localid = i;
+ c->localid = alloc_channel_id();
c->closes = 0;
c->v2.remwindow = ssh2_pkt_getuint32();
c->v2.remmaxpkt = ssh2_pkt_getuint32();
try_send = TRUE;
}
if (try_send) {
- enum234 e;
+ int i;
struct ssh_channel *c;
/*
* Try to send data on all channels if we can.
*/
- for (c = first234(ssh_channels, &e); c; c = next234(&e))
+ for (i = 0; NULL != (c = index234(ssh_channels, i)); i++)
ssh2_try_send(c);
}
}