if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
{ fprintf(stderr, "%s\n", s); fflush(stderr); } }
+/* logevent, only printf-formatted. */
+void logeventf(char *fmt, ...)
+{
+ va_list ap;
+ char stuff[200];
+
+ va_start(ap, fmt);
+ vsprintf(stuff, fmt, ap);
+ va_end(ap);
+ logevent(stuff);
+}
+
#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
(s ? sk_close(s), s = NULL : 0), \
- connection_fatal msg )
+ logeventf msg, connection_fatal msg )
#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
#define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */
#define BUG_CHOKES_ON_SSH1_IGNORE 1
#define BUG_SSH2_HMAC 2
#define BUG_NEEDS_SSH1_PLAIN_PASSWORD 4
+#define BUG_CHOKES_ON_RSA 8
+#define BUG_SSH2_RSA_PADDING 16
+#define BUG_SSH2_DERIVEKEY 32
+#define BUG_SSH2_DH_GEX 64
static int ssh_pkt_ctx = 0;
extern void pfd_unthrottle(Socket s);
extern void pfd_override_throttle(Socket s, int enable);
+static void ssh2_pkt_init(int pkt_type);
+static void ssh2_pkt_addbool(unsigned char value);
+static void ssh2_pkt_adduint32(unsigned long value);
+static void ssh2_pkt_addstring_start(void);
+static void ssh2_pkt_addstring_str(char *data);
+static void ssh2_pkt_addstring_data(char *data, int len);
+static void ssh2_pkt_addstring(char *data);
+static char *ssh2_mpint_fmt(Bignum b, int *len);
+static void ssh2_pkt_addmp(Bignum b);
+static int ssh2_pkt_construct(void);
+static void ssh2_pkt_send(void);
+
/*
* Buffer management constants. There are several of these for
* various different purposes:
struct ssh_channel {
unsigned remoteid, localid;
int type;
+ /*
+ * In SSH1, this value contains four bits:
+ *
+ * 1 We have sent SSH1_MSG_CHANNEL_CLOSE.
+ * 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
+ * 4 We have received SSH1_MSG_CHANNEL_CLOSE.
+ * 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
+ *
+ * A channel is completely finished with when all four bits are set.
+ */
int closes;
union {
struct ssh1_data_channel {
static const struct ssh_kex *kex = NULL;
static const struct ssh_signkey *hostkey = NULL;
static unsigned char ssh2_session_id[20];
-int (*ssh_get_line) (const char *prompt, char *str, int maxlen,
- int is_pw) = NULL;
static char *savedhost;
static int savedport;
st->to_read -= st->chunk;
}
+ if (cipher && detect_attack(pktin.data, st->biglen, NULL)) {
+ bombout(("Network attack (CRC compensation) detected!"));
+ crReturn(0);
+ }
+
if (cipher)
cipher->decrypt(pktin.data, st->biglen);
pktin.type == SSH1_MSG_DEBUG ||
pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
- long strlen = GET_32BIT(pktin.body);
- if (strlen + 4 != pktin.length) {
+ long stringlen = GET_32BIT(pktin.body);
+ if (stringlen + 4 != pktin.length) {
bombout(("Received data packet with bogus string length"));
crReturn(0);
}
if (pktin.type == SSH1_MSG_DEBUG) {
/* log debug message */
- char buf[80];
- int strlen = GET_32BIT(pktin.body);
- strcpy(buf, "Remote: ");
- if (strlen > 70)
- strlen = 70;
- memcpy(buf + 8, pktin.body + 4, strlen);
- buf[8 + strlen] = '\0';
+ char buf[512];
+ int stringlen = GET_32BIT(pktin.body);
+ strcpy(buf, "Remote debug message: ");
+ if (stringlen > 480)
+ stringlen = 480;
+ memcpy(buf + 8, pktin.body + 4, stringlen);
+ buf[8 + stringlen] = '\0';
logevent(buf);
goto next_packet;
} else if (pktin.type == SSH1_MSG_IGNORE) {
msglen = sizeof(buf) - nowlen - 1;
memcpy(buf + nowlen, pktin.body + 4, msglen);
buf[nowlen + msglen] = '\0';
- logevent(buf);
+ /* logevent(buf); (this is now done within the bombout macro) */
bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
crReturn(0);
}
st->pad = pktin.data[4];
/*
+ * _Completely_ silly lengths should be stomped on before they
+ * do us any more damage.
+ */
+ if (st->len < 0 || st->pad < 0 || st->len + st->pad < 0) {
+ bombout(("Incoming packet was garbled on decryption"));
+ crReturn(0);
+ }
+
+ /*
* This enables us to deduce the payload length.
*/
st->payload = st->len - st->pad - 1;
log_packet(PKT_INCOMING, pktin.type, ssh2_pkt_type(pktin.type),
pktin.data+6, pktin.length-6);
- 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);
+ switch (pktin.type) {
+ /*
+ * These packets we must handle instantly.
+ */
+ case 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);
+ bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
+ reason,
+ (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
+ ssh2_disconnect_reasons[reason] : "unknown",
+ buf+nowlen));
+ crReturn(0);
+ }
+ break;
+ case SSH2_MSG_IGNORE:
+ goto next_packet;
+ case SSH2_MSG_DEBUG:
+ {
+ /* log the debug message */
+ char buf[512];
+ /* int display = pktin.body[6]; */
+ int stringlen = GET_32BIT(pktin.data+7);
+ int prefix;
+ strcpy(buf, "Remote debug message: ");
+ prefix = strlen(buf);
+ if (stringlen > sizeof(buf)-prefix-1)
+ stringlen = sizeof(buf)-prefix-1;
+ memcpy(buf + prefix, pktin.data + 11, stringlen);
+ buf[prefix + stringlen] = '\0';
+ logevent(buf);
}
- 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);
- bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
- reason,
- (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
- ssh2_disconnect_reasons[reason] : "unknown",
- buf+nowlen));
- crReturn(0);
+ goto next_packet; /* FIXME: print the debug message */
+
+ /*
+ * These packets we need do nothing about here.
+ */
+ case SSH2_MSG_UNIMPLEMENTED:
+ case SSH2_MSG_SERVICE_REQUEST:
+ case SSH2_MSG_SERVICE_ACCEPT:
+ case SSH2_MSG_KEXINIT:
+ case SSH2_MSG_NEWKEYS:
+ case SSH2_MSG_KEXDH_INIT:
+ case SSH2_MSG_KEXDH_REPLY:
+ /* case SSH2_MSG_KEX_DH_GEX_REQUEST: duplicate case value */
+ /* case SSH2_MSG_KEX_DH_GEX_GROUP: duplicate case value */
+ case SSH2_MSG_KEX_DH_GEX_INIT:
+ case SSH2_MSG_KEX_DH_GEX_REPLY:
+ case SSH2_MSG_USERAUTH_REQUEST:
+ case SSH2_MSG_USERAUTH_FAILURE:
+ case SSH2_MSG_USERAUTH_SUCCESS:
+ case SSH2_MSG_USERAUTH_BANNER:
+ case SSH2_MSG_USERAUTH_PK_OK:
+ /* case SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: duplicate case value */
+ /* case SSH2_MSG_USERAUTH_INFO_REQUEST: duplicate case value */
+ case SSH2_MSG_USERAUTH_INFO_RESPONSE:
+ case SSH2_MSG_GLOBAL_REQUEST:
+ case SSH2_MSG_REQUEST_SUCCESS:
+ case SSH2_MSG_REQUEST_FAILURE:
+ case SSH2_MSG_CHANNEL_OPEN:
+ case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
+ case SSH2_MSG_CHANNEL_OPEN_FAILURE:
+ case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
+ case SSH2_MSG_CHANNEL_DATA:
+ case SSH2_MSG_CHANNEL_EXTENDED_DATA:
+ case SSH2_MSG_CHANNEL_EOF:
+ case SSH2_MSG_CHANNEL_CLOSE:
+ case SSH2_MSG_CHANNEL_REQUEST:
+ case SSH2_MSG_CHANNEL_SUCCESS:
+ case SSH2_MSG_CHANNEL_FAILURE:
+ break;
+
+ /*
+ * For anything else we send SSH2_MSG_UNIMPLEMENTED.
+ */
+ default:
+ ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
+ ssh2_pkt_adduint32(st->incoming_sequence - 1);
+ ssh2_pkt_send();
+ break;
}
crFinish(0);
static void ssh2_pkt_getstring(char **p, int *length)
{
*p = NULL;
+ *length = 0;
if (pktin.length - pktin.savedpos < 4)
return;
*length = GET_32BIT(pktin.data + pktin.savedpos);
}
/*
+ * Helper function to add an SSH2 signature blob to a packet.
+ * Expects to be shown the public key blob as well as the signature
+ * blob. Normally works just like ssh2_pkt_addstring, but will
+ * fiddle with the signature packet if necessary for
+ * BUG_SSH2_RSA_PADDING.
+ */
+static void ssh2_add_sigblob(void *pkblob_v, int pkblob_len,
+ void *sigblob_v, int sigblob_len)
+{
+ unsigned char *pkblob = (unsigned char *)pkblob_v;
+ unsigned char *sigblob = (unsigned char *)sigblob_v;
+
+ /* dmemdump(pkblob, pkblob_len); */
+ /* dmemdump(sigblob, sigblob_len); */
+
+ /*
+ * See if this is in fact an ssh-rsa signature and a buggy
+ * server; otherwise we can just do this the easy way.
+ */
+ if ((ssh_remote_bugs & BUG_SSH2_RSA_PADDING) &&
+ (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
+ int pos, len, siglen;
+
+ /*
+ * Find the byte length of the modulus.
+ */
+
+ pos = 4+7; /* skip over "ssh-rsa" */
+ pos += 4 + GET_32BIT(pkblob+pos); /* skip over exponent */
+ len = GET_32BIT(pkblob+pos); /* find length of modulus */
+ pos += 4; /* find modulus itself */
+ while (len > 0 && pkblob[pos] == 0)
+ len--, pos++;
+ /* debug(("modulus length is %d\n", len)); */
+
+ /*
+ * Now find the signature integer.
+ */
+ pos = 4+7; /* skip over "ssh-rsa" */
+ siglen = GET_32BIT(sigblob+pos);
+ /* debug(("signature length is %d\n", siglen)); */
+
+ if (len != siglen) {
+ unsigned char newlen[4];
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(sigblob, pos);
+ /* dmemdump(sigblob, pos); */
+ pos += 4; /* point to start of actual sig */
+ PUT_32BIT(newlen, len);
+ ssh2_pkt_addstring_data(newlen, 4);
+ /* dmemdump(newlen, 4); */
+ newlen[0] = 0;
+ while (len-- > siglen) {
+ ssh2_pkt_addstring_data(newlen, 1);
+ /* dmemdump(newlen, 1); */
+ }
+ ssh2_pkt_addstring_data(sigblob+pos, siglen);
+ /* dmemdump(sigblob+pos, siglen); */
+ return;
+ }
+
+ /* Otherwise fall through and do it the easy way. */
+ }
+
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(sigblob, sigblob_len);
+}
+
+/*
* Examine the remote side's version string and compare it against
* a list of known buggy implementations.
*/
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, "Cisco-1.25")) {
+ if (cfg.sshbug_ignore1 == BUG_ON ||
+ (cfg.sshbug_ignore1 == BUG_AUTO &&
+ (!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, "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")) {
+ if (cfg.sshbug_plainpw1 == BUG_ON ||
+ (cfg.sshbug_plainpw1 == BUG_AUTO &&
+ (!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
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)) {
+ if (cfg.sshbug_rsa1 == BUG_ON ||
+ (cfg.sshbug_rsa1 == BUG_AUTO &&
+ (!strcmp(imp, "Cisco-1.25")))) {
+ /*
+ * These versions apparently have no clue whatever about
+ * RSA authentication and will panic and die if they see
+ * an AUTH_RSA message.
+ */
+ ssh_remote_bugs |= BUG_CHOKES_ON_RSA;
+ logevent("We believe remote version can't handle RSA authentication");
+ }
+
+ if (cfg.sshbug_hmac2 == BUG_ON ||
+ (cfg.sshbug_hmac2 == BUG_AUTO &&
+ (!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)))) {
/*
* These versions have the HMAC bug.
*/
ssh_remote_bugs |= BUG_SSH2_HMAC;
logevent("We believe remote version has SSH2 HMAC bug");
}
+
+ if (cfg.sshbug_derivekey2 == BUG_ON ||
+ (cfg.sshbug_derivekey2 == BUG_AUTO &&
+ (!strncmp(imp, "2.0.", 4)))) {
+ /*
+ * These versions have the key-derivation bug (failing to
+ * include the literal shared secret in the hashes that
+ * generate the keys).
+ */
+ ssh_remote_bugs |= BUG_SSH2_DERIVEKEY;
+ logevent("We believe remote version has SSH2 key-derivation bug");
+ }
+
+ if (cfg.sshbug_rsapad2 == BUG_ON ||
+ (cfg.sshbug_rsapad2 == BUG_AUTO &&
+ ((!strncmp(imp, "OpenSSH_2.", 10) && imp[10]>='5' && imp[10]<='9') ||
+ (!strncmp(imp, "OpenSSH_3.", 10) && imp[10]>='0' && imp[10]<='2')))){
+ /*
+ * These versions have the SSH2 RSA padding bug.
+ */
+ ssh_remote_bugs |= BUG_SSH2_RSA_PADDING;
+ logevent("We believe remote version has SSH2 RSA padding bug");
+ }
+
+ if (cfg.sshbug_dhgex2 == BUG_ON) {
+ /*
+ * These versions have the SSH2 DH GEX bug.
+ */
+ ssh_remote_bugs |= BUG_SSH2_DH_GEX;
+ logevent("We believe remote version has SSH2 DH group exchange bug");
+ }
}
static int do_ssh_init(unsigned char c)
static int vstrsize;
static char *vlog;
static int i;
+ static int proto1, proto2;
crBegin;
sfree(vlog);
/*
- * Server version "1.99" means we can choose whether we use v1
- * or v2 protocol. Choice is based on cfg.sshprot.
+ * Decide which SSH protocol version to support.
*/
- if (ssh_versioncmp(version, cfg.sshprot == 1 ? "2.0" : "1.99") >= 0) {
+
+ /* Anything strictly below "2.0" means protocol 1 is supported. */
+ proto1 = ssh_versioncmp(version, "2.0") < 0;
+ /* Anything greater or equal to "1.99" means protocol 2 is supported. */
+ proto2 = ssh_versioncmp(version, "1.99") >= 0;
+
+ if (cfg.sshprot == 0 && !proto1) {
+ bombout(("SSH protocol version 1 required by user but not provided by server"));
+ crReturn(0);
+ }
+ if (cfg.sshprot == 3 && !proto2) {
+ bombout(("SSH protocol version 2 required by user but not provided by server"));
+ crReturn(0);
+ }
+
+ if (proto2 && (cfg.sshprot >= 2 || !proto1)) {
/*
- * This is a v2 server. Begin v2 protocol.
+ * Use v2 protocol.
*/
char verstring[80], vlog[100];
sprintf(verstring, "SSH-2.0-%s", sshver);
s_rdpkt = ssh2_rdpkt;
} else {
/*
- * This is a v1 server. Begin v1 protocol.
+ * Use v1 protocol.
*/
char verstring[80], vlog[100];
sprintf(verstring, "SSH-%s-%s",
sprintf(vlog, "We claim version: %s", verstring);
logevent(vlog);
strcat(verstring, "\n");
+
logevent("Using SSH protocol version 1");
sk_write(s, verstring, strlen(verstring));
ssh_protocol = ssh1_protocol;
}
if (error_msg) {
/* A socket error has occurred. */
+ logevent(error_msg);
connection_fatal(error_msg);
} else {
/* Otherwise, the remote side closed the connection normally. */
sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
logevent(buf);
}
- s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
- if ((err = sk_socket_error(s)))
+ s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
+ if ((err = sk_socket_error(s))) {
+ s = NULL;
return err;
+ }
#ifdef FWHACK
sk_write(s, "connect ", 8);
}
/*
+ * Username and password input, abstracted off into reusable
+ * routines (hopefully even reusable between SSH1 and SSH2!).
+ */
+static char *ssh_userpass_input_buffer;
+static int ssh_userpass_input_buflen;
+static int ssh_userpass_input_bufpos;
+static int ssh_userpass_input_echo;
+
+/* Set up a username or password input loop on a given buffer. */
+void setup_userpass_input(char *buffer, int buflen, int echo)
+{
+ ssh_userpass_input_buffer = buffer;
+ ssh_userpass_input_buflen = buflen;
+ ssh_userpass_input_bufpos = 0;
+ ssh_userpass_input_echo = echo;
+}
+
+/*
+ * Process some terminal data in the course of username/password
+ * input. Returns >0 for success (line of input returned in
+ * buffer), <0 for failure (user hit ^C/^D, bomb out and exit), 0
+ * for inconclusive (keep waiting for more input please).
+ */
+int process_userpass_input(unsigned char *in, int inlen)
+{
+ char c;
+
+ while (inlen--) {
+ switch (c = *in++) {
+ case 10:
+ case 13:
+ ssh_userpass_input_buffer[ssh_userpass_input_bufpos] = 0;
+ ssh_userpass_input_buffer[ssh_userpass_input_buflen-1] = 0;
+ return +1;
+ break;
+ case 8:
+ case 127:
+ if (ssh_userpass_input_bufpos > 0) {
+ if (ssh_userpass_input_echo)
+ c_write_str("\b \b");
+ ssh_userpass_input_bufpos--;
+ }
+ break;
+ case 21:
+ case 27:
+ while (ssh_userpass_input_bufpos > 0) {
+ if (ssh_userpass_input_echo)
+ c_write_str("\b \b");
+ ssh_userpass_input_bufpos--;
+ }
+ break;
+ case 3:
+ case 4:
+ return -1;
+ break;
+ default:
+ if (((c >= ' ' && c <= '~') ||
+ ((unsigned char) c >= 160))
+ && ssh_userpass_input_bufpos < ssh_userpass_input_buflen-1) {
+ ssh_userpass_input_buffer[ssh_userpass_input_bufpos++] = c;
+ if (ssh_userpass_input_echo)
+ c_write(&c, 1);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
* Handle the key exchange and user authentication phases.
*/
static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
fflush(stdout);
{
- static int pos = 0;
- static char c;
if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
- if (ssh_get_line) {
+ if (ssh_get_line && !ssh_getline_pw_only) {
if (!ssh_get_line("login as: ",
username, sizeof(username), FALSE)) {
/*
crReturn(1);
}
} else {
+ static int ret;
c_write_str("login as: ");
ssh_send_ok = 1;
- while (pos >= 0) {
+
+ setup_userpass_input(username, sizeof(username), 1);
+ do {
crWaitUntil(!ispkt);
- while (inlen--)
- switch (c = *in++) {
- case 10:
- case 13:
- username[pos] = 0;
- pos = -1;
- break;
- case 8:
- case 127:
- if (pos > 0) {
- c_write_str("\b \b");
- pos--;
- }
- break;
- case 21:
- case 27:
- while (pos > 0) {
- c_write_str("\b \b");
- pos--;
- }
- break;
- case 3:
- case 4:
- random_save_seed();
- exit(0);
- break;
- default:
- if (((c >= ' ' && c <= '~') ||
- ((unsigned char) c >= 160))
- && pos < sizeof(username)-1) {
- username[pos++] = c;
- c_write(&c, 1);
- }
- break;
- }
- }
+ ret = process_userpass_input(in, inlen);
+ } while (ret == 0);
+ if (ret < 0)
+ cleanup_exit(0);
c_write_str("\r\n");
- username[strcspn(username, "\n\r")] = '\0';
}
} else {
- strncpy(username, cfg.username, 99);
- username[99] = '\0';
+ strncpy(username, cfg.username, sizeof(username));
+ username[sizeof(username)-1] = '\0';
}
send_packet(SSH1_CMSG_USER, PKT_STR, username, PKT_END);
crWaitUntil(ispkt);
- tried_publickey = tried_agent = 0;
+ if ((ssh_remote_bugs & BUG_CHOKES_ON_RSA)) {
+ /* We must not attempt PK auth. Pretend we've already tried it. */
+ tried_publickey = tried_agent = 1;
+ } else {
+ tried_publickey = tried_agent = 0;
+ }
tis_auth_refused = ccard_auth_refused = 0;
/* Load the public half of cfg.keyfile so we notice if it's in Pageant */
if (*cfg.keyfile) {
}
if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
char *comment = NULL;
+ int type;
+ char msgbuf[256];
if (flags & FLAG_VERBOSE)
c_write_str("Trying public key authentication.\r\n");
+ sprintf(msgbuf, "Trying public key \"%.200s\"", cfg.keyfile);
+ logevent(msgbuf);
+ type = key_type(cfg.keyfile);
+ if (type != SSH_KEYTYPE_SSH1) {
+ sprintf(msgbuf, "Key is of wrong type (%s)",
+ key_type_to_str(type));
+ logevent(msgbuf);
+ c_write_str(msgbuf);
+ c_write_str("\r\n");
+ tried_publickey = 1;
+ continue;
+ }
if (!rsakey_encrypted(cfg.keyfile, &comment)) {
if (flags & FLAG_VERBOSE)
c_write_str("No passphrase required.\r\n");
send_packet(SSH1_MSG_DISCONNECT,
PKT_STR, "No more passwords available to try",
PKT_END);
+ logevent("Unable to authenticate");
connection_fatal("Unable to authenticate");
ssh_state = SSH_STATE_CLOSED;
crReturn(1);
} else {
/* Prompt may have come from server. We've munged it a bit, so
* we know it to be zero-terminated at least once. */
+ static int ret;
c_write_untrusted(prompt, strlen(prompt));
pos = 0;
- ssh_send_ok = 1;
- while (pos >= 0) {
+
+ setup_userpass_input(password, sizeof(password), 0);
+ do {
crWaitUntil(!ispkt);
- while (inlen--)
- switch (c = *in++) {
- case 10:
- case 13:
- password[pos] = 0;
- pos = -1;
- break;
- case 8:
- case 127:
- if (pos > 0)
- pos--;
- break;
- case 21:
- case 27:
- pos = 0;
- break;
- case 3:
- case 4:
- random_save_seed();
- exit(0);
- break;
- default:
- if (pos < sizeof(password)-1)
- password[pos++] = c;
- break;
- }
- }
+ ret = process_userpass_input(in, inlen);
+ } while (ret == 0);
+ if (ret < 0)
+ cleanup_exit(0);
c_write_str("\r\n");
}
* on it now, and then when the server acks the channel
* open, we can close it then.
*/
- if (c->remoteid != -1) {
+ if (((int)c->remoteid) != -1) {
if (ssh_version == 1) {
send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
ssh2_pkt_send();
}
}
- c->closes = 1;
+ c->closes = 1; /* sent MSG_CLOSE */
if (c->type == CHAN_X11) {
c->u.x11.s = NULL;
logevent("Forwarded X11 connection terminated");
}
{
- char type, *e;
+ char type;
+ static char *e;
int n;
- int sport,dport;
+ int sport,dport,sserv,dserv;
char sports[256], dports[256], host[256];
char buf[1024];
+ struct servent *se;
ssh_rportfwds = newtree234(ssh_rportcmp_ssh1);
/* Add port forwardings. */
dports[n] = 0;
e++;
dport = atoi(dports);
+ dserv = 0;
+ if (dport == 0) {
+ dserv = 1;
+ se = getservbyname(dports, NULL);
+ if (se != NULL) {
+ dport = ntohs(se->s_port);
+ } else {
+ sprintf(buf,
+ "Service lookup failed for destination port \"%s\"",
+ dports);
+ logevent(buf);
+ }
+ }
sport = atoi(sports);
+ sserv = 0;
+ if (sport == 0) {
+ sserv = 1;
+ se = getservbyname(sports, NULL);
+ if (se != NULL) {
+ sport = ntohs(se->s_port);
+ } else {
+ sprintf(buf,
+ "Service lookup failed for source port \"%s\"",
+ sports);
+ logevent(buf);
+ }
+ }
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, sport);
- sprintf(buf, "Local port %d forwarding to %s:%d",
- sport, host, dport);
+ sprintf(buf, "Local port %.*s%.*s%d%.*s forwarding to"
+ " %s:%.*s%.*s%d%.*s",
+ sserv ? strlen(sports) : 0, sports,
+ sserv, "(", sport, sserv, ")",
+ host,
+ dserv ? strlen(dports) : 0, dports,
+ dserv, "(", dport, dserv, ")");
logevent(buf);
} else {
struct ssh_rportfwd *pf;
logevent(buf);
sfree(pf);
} else {
- sprintf(buf, "Requesting remote port %d forward to %s:%d",
- sport, host, dport);
+ sprintf(buf, "Requesting remote port %.*s%.*s%d%.*s"
+ " forward to %s:%.*s%.*s%d%.*s",
+ sserv ? strlen(sports) : 0, sports,
+ sserv, "(", sport, sserv, ")",
+ host,
+ dserv ? strlen(dports) : 0, dports,
+ dserv, "(", dport, dserv, ")");
logevent(buf);
send_packet(SSH1_CMSG_PORT_FORWARD_REQUEST,
PKT_INT, sport,
PKT_STR, host,
PKT_INT, dport,
PKT_END);
+ do {
+ crReturnV;
+ } while (!ispkt);
+ if (pktin.type != SSH1_SMSG_SUCCESS
+ && pktin.type != SSH1_SMSG_FAILURE) {
+ bombout(("Protocol confusion"));
+ crReturnV;
+ } else if (pktin.type == SSH1_SMSG_FAILURE) {
+ c_write_str("Server refused port forwarding\r\n");
+ ssh_editing = ssh_echoing = 1;
+ }
+ logevent("Remote port forwarding enabled");
}
}
}
unsigned i = GET_32BIT(pktin.body);
struct ssh_channel *c;
c = find234(ssh_channels, &i, ssh_channelfind);
- if (c) {
+ if (c && ((int)c->remoteid) != -1) {
int closetype;
closetype =
(pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
- if (!(c->closes & closetype))
- send_packet(pktin.type, PKT_INT, c->remoteid,
- PKT_END);
+
if ((c->closes == 0) && (c->type == CHAN_X11)) {
logevent("Forwarded X11 connection terminated");
assert(c->u.x11.s != NULL);
pfd_close(c->u.pfd.s);
c->u.pfd.s = NULL;
}
- c->closes |= closetype;
- if (c->closes == 3) {
+
+ c->closes |= (closetype << 2); /* seen this message */
+ if (!(c->closes & closetype)) {
+ send_packet(pktin.type, PKT_INT, c->remoteid,
+ PKT_END);
+ c->closes |= closetype; /* sent it too */
+ }
+
+ if (c->closes == 15) {
del234(ssh_channels, c);
sfree(c);
}
+ } else {
+ bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
+ pktin.type == SSH1_MSG_CHANNEL_CLOSE ? "" :
+ "_CONFIRMATION", c ? "half-open" : "nonexistent",
+ i));
}
} else if (pktin.type == SSH1_MSG_CHANNEL_DATA) {
/* Data sent down one of our channels. */
SHA_State s;
/* First 20 bytes. */
SHA_Init(&s);
- sha_mpint(&s, K);
+ if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+ sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, &chr, 1);
SHA_Bytes(&s, sessid, 20);
SHA_Final(&s, keyspace);
/* Next 20 bytes. */
SHA_Init(&s);
- sha_mpint(&s, K);
+ if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+ sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, keyspace, 20);
SHA_Final(&s, keyspace + 20);
/*
* Be prepared to work around the buggy MAC problem.
*/
- if (cfg.buggymac || (ssh_remote_bugs & BUG_SSH2_HMAC))
+ if (ssh_remote_bugs & BUG_SSH2_HMAC)
maclist = buggymacs, nmacs = lenof(buggymacs);
else
maclist = macs, nmacs = lenof(macs);
/* List key exchange algorithms. */
ssh2_pkt_addstring_start();
for (i = 0; i < lenof(kex_algs); i++) {
+ if (kex_algs[i] == &ssh_diffiehellman_gex &&
+ (ssh_remote_bugs & BUG_SSH2_DH_GEX))
+ continue;
ssh2_pkt_addstring_str(kex_algs[i]->name);
if (i < lenof(kex_algs) - 1)
ssh2_pkt_addstring_str(",");
pktin.savedpos += 16; /* skip garbage cookie */
ssh2_pkt_getstring(&str, &len); /* key exchange algorithms */
for (i = 0; i < lenof(kex_algs); i++) {
+ if (kex_algs[i] == &ssh_diffiehellman_gex &&
+ (ssh_remote_bugs & BUG_SSH2_DH_GEX))
+ continue;
if (in_commasep_string(kex_algs[i]->name, str, len)) {
kex = kex_algs[i];
break;
*/
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
{
+ /*
+ * Never send WINDOW_ADJUST for a channel that the remote side
+ * already thinks it's closed; there's no point, since it won't
+ * be sending any more data anyway.
+ */
+ if (c->closes != 0)
+ return;
+
if (newwin > c->v.v2.locwindow) {
ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
ssh2_pkt_adduint32(c->remoteid);
static int tried_pubkey_config, tried_agent, tried_keyb_inter;
static int kbd_inter_running;
static int we_are_in;
- static int num_prompts, echo;
+ static int num_prompts, curr_prompt, echo;
static char username[100];
+ static int got_username;
static char pwprompt[200];
static char password[100];
static void *publickey_blob;
* retype it!
*/
username[0] = '\0';
+ got_username = FALSE;
do {
- static int pos;
- static char c;
-
/*
* Get a username.
*/
- pos = 0;
- if (*username && !cfg.change_username) {
+ if (got_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 && !ssh_getline_pw_only) {
if (!ssh_get_line("login as: ",
username, sizeof(username), FALSE)) {
/*
crReturnV;
}
} else {
+ static int ret;
c_write_str("login as: ");
ssh_send_ok = 1;
- while (pos >= 0) {
+ setup_userpass_input(username, sizeof(username), 1);
+ do {
crWaitUntilV(!ispkt);
- while (inlen--)
- switch (c = *in++) {
- case 10:
- case 13:
- username[pos] = 0;
- pos = -1;
- break;
- case 8:
- case 127:
- if (pos > 0) {
- c_write_str("\b \b");
- pos--;
- }
- break;
- case 21:
- case 27:
- while (pos > 0) {
- c_write_str("\b \b");
- pos--;
- }
- break;
- case 3:
- case 4:
- random_save_seed();
- exit(0);
- break;
- default:
- if (((c >= ' ' && c <= '~') ||
- ((unsigned char) c >= 160))
- && pos < sizeof(username)-1) {
- username[pos++] = c;
- c_write(&c, 1);
- }
- break;
- }
- }
+ ret = process_userpass_input(in, inlen);
+ } while (ret == 0);
+ if (ret < 0)
+ cleanup_exit(0);
}
c_write_str("\r\n");
username[strcspn(username, "\n\r")] = '\0';
} else {
char stuff[200];
- strncpy(username, cfg.username, 99);
- username[99] = '\0';
+ strncpy(username, cfg.username, sizeof(username));
+ username[sizeof(username)-1] = '\0';
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
sprintf(stuff, "Using username \"%s\".\r\n", username);
c_write_str(stuff);
}
}
+ got_username = TRUE;
/*
* Send an authentication request using method "none": (a)
kbd_inter_running = FALSE;
/* Load the pub half of cfg.keyfile so we notice if it's in Pageant */
if (*cfg.keyfile) {
- publickey_blob = ssh2_userkey_loadpub(cfg.keyfile, NULL,
- &publickey_bloblen);
+ int keytype;
+ logeventf("Reading private key file \"%.150s\"", cfg.keyfile);
+ keytype = key_type(cfg.keyfile);
+ if (keytype == SSH_KEYTYPE_SSH2)
+ publickey_blob = ssh2_userkey_loadpub(cfg.keyfile, NULL,
+ &publickey_bloblen);
+ else {
+ char msgbuf[256];
+ logeventf("Unable to use this key file (%s)",
+ key_type_to_str(keytype));
+ sprintf(msgbuf, "Unable to use key file \"%.150s\" (%s)\r\n",
+ cfg.keyfile, key_type_to_str(keytype));
+ c_write_str(msgbuf);
+ publickey_blob = NULL;
+ }
} else
publickey_blob = NULL;
if (kbd_inter_running &&
pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
/*
- * This is a further prompt in keyboard-interactive
- * authentication. Do nothing.
+ * This is either a further set-of-prompts packet
+ * in keyboard-interactive authentication, or it's
+ * the same one and we came back here with `gotit'
+ * set. In the former case, we must reset the
+ * curr_prompt variable.
*/
+ if (!gotit)
+ curr_prompt = 0;
} else if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
bombout(("Strange packet received during authentication: type %d",
pktin.type));
method = 0;
ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+ /*
+ * Most password/passphrase prompts will be
+ * non-echoing, so we set this to 0 by default.
+ * Exception is that some keyboard-interactive prompts
+ * can be echoing, in which case we'll set this to 1.
+ */
+ echo = 0;
+
if (!method && can_pubkey && agent_exists() && !tried_agent) {
/*
* Attempt public-key authentication using Pageant.
static int authed = FALSE;
void *r;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
tried_agent = TRUE;
if (ret) {
if (ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
logevent("Sending Pageant's response");
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(ret + 9,
- GET_32BIT(ret +
- 5));
+ ssh2_add_sigblob(pkblob, pklen,
+ ret + 9, GET_32BIT(ret + 5));
ssh2_pkt_send();
authed = TRUE;
break;
}
}
- if (!method && can_pubkey && *cfg.keyfile
+ if (!method && can_pubkey && publickey_blob
&& !tried_pubkey_config) {
unsigned char *pub_blob;
char *algorithm, *comment;
tried_pubkey_config = TRUE;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
/*
type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
tried_keyb_inter = TRUE;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
}
kbd_inter_running = TRUE;
+ curr_prompt = 0;
}
if (kbd_inter_running) {
type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
tried_keyb_inter = TRUE;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
- /* We've got packet with that "interactive" info
- dump banners, and set its prompt as ours */
- {
- char *name, *inst, *lang, *prompt;
- int name_len, inst_len, lang_len, prompt_len;
+ if (curr_prompt == 0) {
+ /*
+ * We've got a fresh USERAUTH_INFO_REQUEST.
+ * Display header data, and start going through
+ * the prompts.
+ */
+ char *name, *inst, *lang;
+ int name_len, inst_len, lang_len;
+
ssh2_pkt_getstring(&name, &name_len);
ssh2_pkt_getstring(&inst, &inst_len);
ssh2_pkt_getstring(&lang, &lang_len);
- if (name_len > 0)
+ if (name_len > 0) {
c_write_untrusted(name, name_len);
- if (inst_len > 0)
+ c_write_str("\r\n");
+ }
+ if (inst_len > 0) {
c_write_untrusted(inst, inst_len);
+ c_write_str("\r\n");
+ }
num_prompts = ssh2_pkt_getuint32();
+ }
- 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;
+ /*
+ * If there are prompts remaining in the packet,
+ * display one and get a response.
+ */
+ if (curr_prompt < num_prompts) {
+ char *prompt;
+ int prompt_len;
+ ssh2_pkt_getstring(&prompt, &prompt_len);
+ if (prompt_len > 0) {
+ strncpy(pwprompt, prompt, sizeof(pwprompt));
+ pwprompt[prompt_len < sizeof(pwprompt) ?
+ prompt_len : sizeof(pwprompt)-1] = '\0';
+ } else {
+ strcpy(pwprompt,
+ "<server failed to send prompt>: ");
+ }
echo = ssh2_pkt_getbool();
- }
+ need_pw = TRUE;
+ } else
+ need_pw = FALSE;
}
if (!method && can_passwd) {
method = AUTH_PASSWORD;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
ssh_pkt_ctx |= SSH2_PKTCTX_PASSWORD;
sprintf(pwprompt, "%.90s@%.90s's password: ", username,
savedhost);
("No more passwords available to try");
ssh2_pkt_addstring("en"); /* language tag */
ssh2_pkt_send();
+ logevent("Unable to authenticate");
connection_fatal("Unable to authenticate");
ssh_state = SSH_STATE_CLOSED;
crReturnV;
}
} else {
- static int pos = 0;
- static char c;
-
+ static int ret;
c_write_untrusted(pwprompt, strlen(pwprompt));
ssh_send_ok = 1;
- pos = 0;
- while (pos >= 0) {
+ setup_userpass_input(password, sizeof(password), echo);
+ do {
crWaitUntilV(!ispkt);
- while (inlen--)
- switch (c = *in++) {
- case 10:
- case 13:
- password[pos] = 0;
- pos = -1;
- break;
- case 8:
- case 127:
- if (pos > 0)
- pos--;
- break;
- case 21:
- case 27:
- pos = 0;
- break;
- case 3:
- case 4:
- random_save_seed();
- exit(0);
- break;
- default:
- if (pos < sizeof(password)-1)
- password[pos++] = c;
- break;
- }
- }
+ ret = process_userpass_input(in, inlen);
+ } while (ret == 0);
+ if (ret < 0)
+ cleanup_exit(0);
c_write_str("\r\n");
}
}
ssh2_pkt_send();
type = AUTH_TYPE_NONE;
} else {
- unsigned char *blob, *sigdata;
- int blob_len, sigdata_len;
+ unsigned char *pkblob, *sigblob, *sigdata;
+ int pkblob_len, sigblob_len, sigdata_len;
/*
* We have loaded the private key and the server
ssh2_pkt_addstring("publickey"); /* method */
ssh2_pkt_addbool(TRUE);
ssh2_pkt_addstring(key->alg->name);
- blob = key->alg->public_blob(key->data, &blob_len);
+ pkblob = key->alg->public_blob(key->data, &pkblob_len);
ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(blob, blob_len);
- sfree(blob);
+ ssh2_pkt_addstring_data(pkblob, pkblob_len);
/*
* The data to be signed is:
memcpy(sigdata + 4, ssh2_session_id, 20);
memcpy(sigdata + 24, pktout.data + 5,
pktout.length - 5);
- blob =
- key->alg->sign(key->data, sigdata, sigdata_len,
- &blob_len);
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(blob, blob_len);
- sfree(blob);
+ sigblob = key->alg->sign(key->data, sigdata,
+ sigdata_len, &sigblob_len);
+ ssh2_add_sigblob(pkblob, pkblob_len,
+ sigblob, sigblob_len);
+ sfree(pkblob);
+ sfree(sigblob);
sfree(sigdata);
ssh2_pkt_send();
logevent("Sent password");
type = AUTH_TYPE_PASSWORD;
} else if (method == AUTH_KEYBOARD_INTERACTIVE) {
- ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
- ssh2_pkt_adduint32(num_prompts);
- ssh2_pkt_addstring(password);
- memset(password, 0, sizeof(password));
- ssh2_pkt_send();
+ if (curr_prompt == 0) {
+ ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
+ ssh2_pkt_adduint32(num_prompts);
+ }
+ if (need_pw) { /* only add pw if we just got one! */
+ ssh2_pkt_addstring(password);
+ memset(password, 0, sizeof(password));
+ curr_prompt++;
+ }
+ if (curr_prompt >= num_prompts) {
+ ssh2_pkt_send();
+ } else {
+ /*
+ * If there are prompts remaining, we set
+ * `gotit' so that we won't attempt to get
+ * another packet. Then we go back round the
+ * loop and will end up retrieving another
+ * prompt out of the existing packet. Funky or
+ * what?
+ */
+ gotit = TRUE;
+ }
type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
} else {
c_write_str
static char *e; /* preserve across crReturn */
char type;
int n;
- int sport,dport;
+ int sport,dport,sserv,dserv;
char sports[256], dports[256], host[256];
char buf[1024];
+ struct servent *se;
ssh_rportfwds = newtree234(ssh_rportcmp_ssh2);
/* Add port forwardings. */
dports[n] = 0;
e++;
dport = atoi(dports);
+ dserv = 0;
+ if (dport == 0) {
+ dserv = 1;
+ se = getservbyname(dports, NULL);
+ if (se != NULL) {
+ dport = ntohs(se->s_port);
+ } else {
+ sprintf(buf,
+ "Service lookup failed for destination port \"%s\"",
+ dports);
+ logevent(buf);
+ }
+ }
sport = atoi(sports);
+ sserv = 0;
+ if (sport == 0) {
+ sserv = 1;
+ se = getservbyname(sports, NULL);
+ if (se != NULL) {
+ sport = ntohs(se->s_port);
+ } else {
+ sprintf(buf,
+ "Service lookup failed for source port \"%s\"",
+ sports);
+ logevent(buf);
+ }
+ }
if (sport && dport) {
if (type == 'L') {
pfd_addforward(host, dport, sport);
- sprintf(buf, "Local port %d forwarding to %s:%d",
- sport, host, dport);
+ sprintf(buf, "Local port %.*s%.*s%d%.*s forwarding to"
+ " %s:%.*s%.*s%d%.*s",
+ sserv ? strlen(sports) : 0, sports,
+ sserv, "(", sport, sserv, ")",
+ host,
+ dserv ? strlen(dports) : 0, dports,
+ dserv, "(", dport, dserv, ")");
logevent(buf);
} else {
struct ssh_rportfwd *pf;
logevent(buf);
sfree(pf);
} else {
- sprintf(buf, "Requesting remote port %d (forwarded to %s:%d)",
- sport, host, dport);
+ sprintf(buf, "Requesting remote port %.*s%.*s%d%.*s"
+ " forward to %s:%.*s%.*s%d%.*s",
+ sserv ? strlen(sports) : 0, sports,
+ sserv, "(", sport, sserv, ")",
+ host,
+ dserv ? strlen(dports) : 0, dports,
+ dserv, "(", dport, dserv, ")");
logevent(buf);
ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
ssh2_pkt_addstring("tcpip-forward");
struct ssh_channel *c;
c = find234(ssh_channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
+ if (!c || ((int)c->remoteid) == -1) {
+ bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
+ c ? "half-open" : "nonexistent", i));
+ }
/* Do pre-close processing on the channel. */
switch (c->type) {
case CHAN_MAINSESSION:
c->type = CHAN_SOCKDATA;
c->v.v2.remwindow = ssh2_pkt_getuint32();
c->v.v2.remmaxpkt = ssh2_pkt_getuint32();
- bufchain_init(&c->v.v2.outbuffer);
if (c->u.pfd.s)
pfd_confirm(c->u.pfd.s);
if (c->closes) {
ssh2_pkt_addstring(buf);
ssh2_pkt_addstring("en"); /* language tag */
ssh2_pkt_send();
- connection_fatal(buf);
+ connection_fatal("%s", buf);
ssh_state = SSH_STATE_CLOSED;
crReturnV;
}
ssh2_pkt_send();
}
}
+ } else if (pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
+ char *type;
+ int typelen, want_reply;
+
+ ssh2_pkt_getstring(&type, &typelen);
+ want_reply = ssh2_pkt_getbool();
+
+ /*
+ * We currently don't support any global requests
+ * at all, so we either ignore the request or
+ * respond with REQUEST_FAILURE, depending on
+ * want_reply.
+ */
+ if (want_reply) {
+ ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
+ ssh2_pkt_send();
+ }
} else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
char *type;
int typelen;
if (ssh_state == SSH_STATE_CLOSED
|| ssh_state == SSH_STATE_PREPACKET) return;
if (ssh_version == 1) {
- send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
+ if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
+ send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
} else {
ssh2_pkt_init(SSH2_MSG_IGNORE);
ssh2_pkt_addstring_start();
c->closes = 0;
c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
c->u.pfd.s = s;
+ bufchain_init(&c->v.v2.outbuffer);
add234(ssh_channels, c);
}
return c;