static unsigned char *deferred_send_data = NULL;
static int deferred_len = 0, deferred_size = 0;
+/*
+ * Gross hack: pscp will try to start SFTP but fall back to scp1 if
+ * that fails. This variable is the means by which scp.c can reach
+ * into the SSH code and find out which one it got.
+ */
+int ssh_fallback_cmd = 0;
+
static int ssh_version;
static int ssh1_throttle_count;
static int ssh_overall_bufsize;
{
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
- int i;
+
if (a->sport > b->sport)
return +1;
if (a->sport < b->sport)
logevent("Encrypted session key");
- switch (cfg.cipher) {
- case CIPHER_BLOWFISH:
- cipher_type = SSH_CIPHER_BLOWFISH;
- break;
- case CIPHER_DES:
- cipher_type = SSH_CIPHER_DES;
- break;
- case CIPHER_3DES:
- cipher_type = SSH_CIPHER_3DES;
- break;
- case CIPHER_AES:
- c_write_str("AES not supported in SSH1, falling back to 3DES\r\n");
- cipher_type = SSH_CIPHER_3DES;
- break;
- }
- 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"));
+ {
+ int cipher_chosen = 0, warn = 0;
+ char *cipher_string = NULL;
+ for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
+ int next_cipher = cfg.ssh_cipherlist[i];
+ if (next_cipher == CIPHER_WARN) {
+ /* If/when we choose a cipher, warn about it */
+ warn = 1;
+ } else if (next_cipher == CIPHER_AES) {
+ /* XXX Probably don't need to mention this. */
+ logevent("AES not supported in SSH1, skipping");
+ } else {
+ switch (next_cipher) {
+ case CIPHER_3DES: cipher_type = SSH_CIPHER_3DES;
+ cipher_string = "3DES"; break;
+ case CIPHER_BLOWFISH: cipher_type = SSH_CIPHER_BLOWFISH;
+ cipher_string = "Blowfish"; break;
+ case CIPHER_DES: cipher_type = SSH_CIPHER_DES;
+ cipher_string = "single-DES"; break;
+ }
+ if (supported_ciphers_mask & (1 << cipher_type))
+ cipher_chosen = 1;
+ }
+ }
+ if (!cipher_chosen) {
+ if ((supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
+ bombout(("Server violates SSH 1 protocol by not "
+ "supporting 3DES encryption"));
+ else
+ /* shouldn't happen */
+ bombout(("No supported ciphers found"));
crReturn(0);
}
+
+ /* Warn about chosen cipher if necessary. */
+ if (warn)
+ askcipher(cipher_string, 0);
}
+
switch (cipher_type) {
case SSH_CIPHER_3DES:
logevent("Using 3DES encryption");
zlib_decompress_init();
}
- 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");
+ /*
+ * Start the shell or command.
+ *
+ * Special case: if the first-choice command is an SSH2
+ * subsystem (hence not usable here) and the second choice
+ * exists, we fall straight back to that.
+ */
+ {
+ char *cmd = cfg.remote_cmd_ptr;
+
+ if (cfg.ssh_subsys && cfg.remote_cmd_ptr2) {
+ cmd = cfg.remote_cmd_ptr2;
+ ssh_fallback_cmd = TRUE;
+ }
+ if (*cmd)
+ send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);
+ else
+ send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
+ logevent("Started session");
+ }
ssh_state = SSH_STATE_SESSION;
if (size_needed)
pfd_confirm(c->u.pfd.s);
}
+ } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) {
+ unsigned int remoteid = GET_32BIT(pktin.body);
+ unsigned int localid = GET_32BIT(pktin.body+4);
+ struct ssh_channel *c;
+
+ c = find234(ssh_channels, &remoteid, ssh_channelfind);
+ if (c && c->type == CHAN_SOCKDATA_DORMANT) {
+ logevent("Forwarded connection refused by server");
+ pfd_close(c->u.pfd.s);
+ del234(ssh_channels, c);
+ sfree(c);
+ }
+
} else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
/* Remote side closes a channel. */
*/
static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
{
- static int i, j, len, nbits, pbits;
+ static int i, j, len, nbits, pbits, warn;
static char *str;
static Bignum p, g, e, f, K;
static int kex_init_value, kex_reply_value;
static void *hkey; /* actual host key */
static unsigned char exchange_hash[20];
static unsigned char keyspace[40];
- static const struct ssh2_ciphers *preferred_cipher;
+ static int n_preferred_ciphers;
+ static const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
static const struct ssh_compress *preferred_comp;
static int first_kex;
first_kex = 1;
/*
- * Set up the preferred cipher and compression.
+ * Set up the preferred ciphers. (NULL => warn below here)
*/
- if (cfg.cipher == CIPHER_BLOWFISH) {
- preferred_cipher = &ssh2_blowfish;
- } else if (cfg.cipher == CIPHER_DES) {
- logevent("Single DES not supported in SSH2; using 3DES");
- preferred_cipher = &ssh2_3des;
- } else if (cfg.cipher == CIPHER_3DES) {
- preferred_cipher = &ssh2_3des;
- } else if (cfg.cipher == CIPHER_AES) {
- preferred_cipher = &ssh2_aes;
- } else {
- /* Shouldn't happen, but we do want to initialise to _something_. */
- preferred_cipher = &ssh2_3des;
+ n_preferred_ciphers = 0;
+ for (i = 0; i < CIPHER_MAX; i++) {
+ switch (cfg.ssh_cipherlist[i]) {
+ case CIPHER_BLOWFISH:
+ preferred_ciphers[n_preferred_ciphers] = &ssh2_blowfish;
+ n_preferred_ciphers++;
+ break;
+ case CIPHER_DES:
+ /* Not supported in SSH2; silently drop */
+ break;
+ case CIPHER_3DES:
+ preferred_ciphers[n_preferred_ciphers] = &ssh2_3des;
+ n_preferred_ciphers++;
+ break;
+ case CIPHER_AES:
+ preferred_ciphers[n_preferred_ciphers] = &ssh2_aes;
+ n_preferred_ciphers++;
+ break;
+ case CIPHER_WARN:
+ /* Flag for later. Don't bother if it's the last in
+ * the list. */
+ if (i < CIPHER_MAX - 1) {
+ preferred_ciphers[n_preferred_ciphers] = NULL;
+ n_preferred_ciphers++;
+ }
+ break;
+ }
}
+
+ /*
+ * Set up preferred compression.
+ */
if (cfg.compression)
preferred_comp = &ssh_zlib;
else
}
/* List client->server encryption algorithms. */
ssh2_pkt_addstring_start();
- for (i = 0; i < lenof(ciphers) + 1; i++) {
- const struct ssh2_ciphers *c =
- i == 0 ? preferred_cipher : ciphers[i - 1];
+ 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 < lenof(ciphers) || j < c->nciphers - 1)
+ if (i < n_preferred_ciphers || j < c->nciphers - 1)
ssh2_pkt_addstring_str(",");
}
}
/* List server->client encryption algorithms. */
ssh2_pkt_addstring_start();
- for (i = 0; i < lenof(ciphers) + 1; i++) {
- const struct ssh2_ciphers *c =
- i == 0 ? preferred_cipher : ciphers[i - 1];
+ 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 < lenof(ciphers) || j < c->nciphers - 1)
+ if (i < n_preferred_ciphers || j < c->nciphers - 1)
ssh2_pkt_addstring_str(",");
}
}
}
}
ssh2_pkt_getstring(&str, &len); /* client->server cipher */
- for (i = 0; i < lenof(ciphers) + 1; i++) {
- const struct ssh2_ciphers *c =
- i == 0 ? preferred_cipher : ciphers[i - 1];
- for (j = 0; j < c->nciphers; j++) {
- if (in_commasep_string(c->list[j]->name, str, len)) {
- cscipher_tobe = c->list[j];
- break;
+ warn = 0;
+ for (i = 0; i < n_preferred_ciphers; i++) {
+ const struct ssh2_ciphers *c = preferred_ciphers[i];
+ if (!c) {
+ warn = 1;
+ } else {
+ for (j = 0; j < c->nciphers; j++) {
+ if (in_commasep_string(c->list[j]->name, str, len)) {
+ cscipher_tobe = c->list[j];
+ break;
+ }
}
}
- if (cscipher_tobe)
+ if (cscipher_tobe) {
+ if (warn)
+ askcipher(cscipher_tobe->name, 1);
break;
+ }
}
ssh2_pkt_getstring(&str, &len); /* server->client cipher */
- for (i = 0; i < lenof(ciphers) + 1; i++) {
- const struct ssh2_ciphers *c =
- i == 0 ? preferred_cipher : ciphers[i - 1];
- for (j = 0; j < c->nciphers; j++) {
- if (in_commasep_string(c->list[j]->name, str, len)) {
- sccipher_tobe = c->list[j];
- break;
+ warn = 0;
+ for (i = 0; i < n_preferred_ciphers; i++) {
+ const struct ssh2_ciphers *c = preferred_ciphers[i];
+ if (!c) {
+ warn = 1;
+ } else {
+ for (j = 0; j < c->nciphers; j++) {
+ if (in_commasep_string(c->list[j]->name, str, len)) {
+ sccipher_tobe = c->list[j];
+ break;
+ }
}
}
- if (sccipher_tobe)
+ if (sccipher_tobe) {
+ if (warn)
+ askcipher(sccipher_tobe->name, 2);
break;
+ }
}
ssh2_pkt_getstring(&str, &len); /* client->server mac */
for (i = 0; i < nmacs; i++) {
in_commasep_string("publickey", methods, methlen);
can_passwd =
in_commasep_string("password", methods, methlen);
- can_passwd =
- in_commasep_string("password", methods, methlen);
can_keyb_inter =
in_commasep_string("keyboard-interactive", methods, methlen);
}
method = 0;
- if (!method && can_keyb_inter && !tried_keyb_inter) {
- method = AUTH_KEYBOARD_INTERACTIVE;
- type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
- tried_keyb_inter = TRUE;
-
- ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- ssh2_pkt_addstring(username);
- ssh2_pkt_addstring("ssh-connection"); /* service requested */
- ssh2_pkt_addstring("keyboard-interactive"); /* method */
- ssh2_pkt_addstring(""); /* lang */
- ssh2_pkt_addstring("");
- ssh2_pkt_send();
-
- crWaitUntilV(ispkt);
- if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
- if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
- gotit = TRUE;
- logevent("Keyboard-interactive authentication refused");
- type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
- continue;
- }
-
- kbd_inter_running = TRUE;
- }
-
- if (kbd_inter_running) {
- method = AUTH_KEYBOARD_INTERACTIVE;
- type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
- tried_keyb_inter = TRUE;
-
- /* 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;
- ssh2_pkt_getstring(&name, &name_len);
- ssh2_pkt_getstring(&inst, &inst_len);
- ssh2_pkt_getstring(&lang, &lang_len);
- if (name_len > 0)
- c_write_untrusted(name, name_len);
- if (inst_len > 0)
- c_write_untrusted(inst, inst_len);
- num_prompts = ssh2_pkt_getuint32();
-
- ssh2_pkt_getstring(&prompt, &prompt_len);
- strncpy(pwprompt, prompt, sizeof(pwprompt));
- need_pw = TRUE;
-
- echo = ssh2_pkt_getbool();
- }
- }
-
if (!method && can_pubkey && agent_exists() && !tried_agent) {
/*
* Attempt public-key authentication using Pageant.
}
}
+ if (!method && can_keyb_inter && !tried_keyb_inter) {
+ method = AUTH_KEYBOARD_INTERACTIVE;
+ type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+ tried_keyb_inter = TRUE;
+
+ ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+ ssh2_pkt_addstring(username);
+ ssh2_pkt_addstring("ssh-connection"); /* service requested */
+ ssh2_pkt_addstring("keyboard-interactive"); /* method */
+ ssh2_pkt_addstring(""); /* lang */
+ ssh2_pkt_addstring("");
+ ssh2_pkt_send();
+
+ crWaitUntilV(ispkt);
+ if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
+ if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
+ gotit = TRUE;
+ logevent("Keyboard-interactive authentication refused");
+ type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+ continue;
+ }
+
+ kbd_inter_running = TRUE;
+ }
+
+ if (kbd_inter_running) {
+ method = AUTH_KEYBOARD_INTERACTIVE;
+ type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+ tried_keyb_inter = TRUE;
+
+ /* 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;
+ ssh2_pkt_getstring(&name, &name_len);
+ ssh2_pkt_getstring(&inst, &inst_len);
+ ssh2_pkt_getstring(&lang, &lang_len);
+ if (name_len > 0)
+ c_write_untrusted(name, name_len);
+ if (inst_len > 0)
+ c_write_untrusted(inst, inst_len);
+ num_prompts = ssh2_pkt_getuint32();
+
+ ssh2_pkt_getstring(&prompt, &prompt_len);
+ strncpy(pwprompt, prompt, sizeof(pwprompt));
+ need_pw = TRUE;
+
+ echo = ssh2_pkt_getbool();
+ }
+ }
+
if (!method && can_passwd) {
method = AUTH_PASSWORD;
sprintf(pwprompt, "%.90s@%.90s's password: ", username,
}
/*
- * Start a shell or a remote command.
+ * Start a shell or a remote command. We may have to attempt
+ * this twice if the config data has provided a second choice
+ * of command.
*/
- ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
- ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
- if (cfg.ssh_subsys) {
- ssh2_pkt_addstring("subsystem");
- ssh2_pkt_addbool(1); /* want reply */
- 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_ptr);
- } else {
- ssh2_pkt_addstring("shell");
- ssh2_pkt_addbool(1); /* want reply */
- }
- ssh2_pkt_send();
- do {
- crWaitUntilV(ispkt);
- if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- unsigned i = ssh2_pkt_getuint32();
- struct ssh_channel *c;
- c = find234(ssh_channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
- c->v.v2.remwindow += ssh2_pkt_getuint32();
+ while (1) {
+ int subsys;
+ char *cmd;
+
+ if (ssh_fallback_cmd) {
+ subsys = cfg.ssh_subsys2;
+ cmd = cfg.remote_cmd_ptr2;
+ } else {
+ subsys = cfg.ssh_subsys;
+ cmd = cfg.remote_cmd_ptr;
+ }
+
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ if (subsys) {
+ ssh2_pkt_addstring("subsystem");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cmd);
+ } else if (*cmd) {
+ ssh2_pkt_addstring("exec");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cmd);
+ } else {
+ ssh2_pkt_addstring("shell");
+ ssh2_pkt_addbool(1); /* want reply */
}
- } 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"));
+ ssh2_pkt_send();
+ do {
+ crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ c->v.v2.remwindow += ssh2_pkt_getuint32();
+ }
+ } 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"));
+ crReturnV;
+ }
+ /*
+ * We failed to start the command. If this is the
+ * fallback command, we really are finished; if it's
+ * not, and if the fallback command exists, try falling
+ * back to it before complaining.
+ */
+ if (!ssh_fallback_cmd && cfg.remote_cmd_ptr2 != NULL) {
+ logevent("Primary command failed; attempting fallback");
+ ssh_fallback_cmd = TRUE;
+ continue;
+ }
+ bombout(("Server refused to start a shell/command"));
crReturnV;
+ } else {
+ logevent("Started a shell/command");
}
- bombout(("Server refused to start a shell/command"));
- crReturnV;
- } else {
- logevent("Started a shell/command");
+ break;
}
ssh_state = SSH_STATE_SESSION;
c = find234(ssh_channels, &i, ssh_channelfind);
if (!c)
continue; /* nonexistent channel */
- if (c->closes == 0) {
- ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(c->remoteid);
- ssh2_pkt_send();
- }
/* Do pre-close processing on the channel. */
switch (c->type) {
case CHAN_MAINSESSION:
break; /* nothing to see here, move along */
case CHAN_X11:
+ if (c->u.x11.s != NULL)
+ x11_close(c->u.x11.s);
+ sshfwd_close(c);
break;
case CHAN_AGENT:
+ sshfwd_close(c);
break;
case CHAN_SOCKDATA:
+ if (c->u.pfd.s != NULL)
+ pfd_close(c->u.pfd.s);
+ sshfwd_close(c);
break;
}
+ if (c->closes == 0) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_send();
+ }
del234(ssh_channels, c);
bufchain_clear(&c->v.v2.outbuffer);
sfree(c);
c->v.v2.remmaxpkt = ssh2_pkt_getuint32();
bufchain_init(&c->v.v2.outbuffer);
pfd_confirm(c->u.pfd.s);
+ } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+ unsigned i = ssh2_pkt_getuint32();
+ struct ssh_channel *c;
+ c = find234(ssh_channels, &i, ssh_channelfind);
+ if (!c)
+ continue; /* nonexistent channel */
+ if (c->type != CHAN_SOCKDATA_DORMANT)
+ continue; /* dunno why they're failing this */
+
+ logevent("Forwarded connection refused by server");
+
+ pfd_close(c->u.pfd.s);
+
+ del234(ssh_channels, c);
+ sfree(c);
} else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
char *type;
int typelen;
ssh_echoing = 0;
ssh1_throttle_count = 0;
ssh_overall_bufsize = 0;
+ ssh_fallback_cmd = 0;
p = connect_to_host(host, port, realhost);
if (p != NULL)