*/
#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) | \
(*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;
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);
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;
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
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)) {
crReturn(1); /* get another character */
}
- vstring = smalloc(16);
vstrsize = 16;
+ vstring = smalloc(vstrsize);
strcpy(vstring, "SSH-");
vslen = 4;
i = 0;
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);
/*
* 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;
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);
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) {
* 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;
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");
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");
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");
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");
} 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;
}
/*
/*
* 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;