#define TRUE 1
#endif
-/* uncomment this for packet level debugging */
-/* #define DUMP_PACKETS */
-
#define logevent(s) { logevent(s); \
if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
{ fprintf(stderr, "%s\n", s); fflush(stderr); } }
#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */
#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */
+/*
+ * Packet type contexts, so that ssh2_pkt_type can correctly decode
+ * the ambiguous type numbers back into the correct type strings.
+ */
+#define SSH2_PKTCTX_DHGROUP1 0x0001
+#define SSH2_PKTCTX_DHGEX 0x0002
+#define SSH2_PKTCTX_PUBLICKEY 0x0010
+#define SSH2_PKTCTX_PASSWORD 0x0020
+#define SSH2_PKTCTX_KBDINTER 0x0040
+#define SSH2_PKTCTX_AUTH_MASK 0x00F0
+
#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 /* 0x1 */
#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 /* 0x2 */
#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 /* 0x3 */
#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
+static int ssh_pkt_ctx = 0;
+
+#define translate(x) if (type == x) return #x
+#define translatec(x,ctx) if (type == x && (ssh_pkt_ctx & ctx)) return #x
+char *ssh1_pkt_type(int type)
+{
+ translate(SSH1_MSG_DISCONNECT);
+ translate(SSH1_SMSG_PUBLIC_KEY);
+ translate(SSH1_CMSG_SESSION_KEY);
+ translate(SSH1_CMSG_USER);
+ translate(SSH1_CMSG_AUTH_RSA);
+ translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_RSA_RESPONSE);
+ translate(SSH1_CMSG_AUTH_PASSWORD);
+ translate(SSH1_CMSG_REQUEST_PTY);
+ translate(SSH1_CMSG_WINDOW_SIZE);
+ translate(SSH1_CMSG_EXEC_SHELL);
+ translate(SSH1_CMSG_EXEC_CMD);
+ translate(SSH1_SMSG_SUCCESS);
+ translate(SSH1_SMSG_FAILURE);
+ translate(SSH1_CMSG_STDIN_DATA);
+ translate(SSH1_SMSG_STDOUT_DATA);
+ translate(SSH1_SMSG_STDERR_DATA);
+ translate(SSH1_CMSG_EOF);
+ translate(SSH1_SMSG_EXIT_STATUS);
+ translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+ translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);
+ translate(SSH1_MSG_CHANNEL_DATA);
+ translate(SSH1_MSG_CHANNEL_CLOSE);
+ translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
+ translate(SSH1_SMSG_X11_OPEN);
+ translate(SSH1_CMSG_PORT_FORWARD_REQUEST);
+ translate(SSH1_MSG_PORT_OPEN);
+ translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);
+ translate(SSH1_SMSG_AGENT_OPEN);
+ translate(SSH1_MSG_IGNORE);
+ translate(SSH1_CMSG_EXIT_CONFIRMATION);
+ translate(SSH1_CMSG_X11_REQUEST_FORWARDING);
+ translate(SSH1_CMSG_AUTH_RHOSTS_RSA);
+ translate(SSH1_MSG_DEBUG);
+ translate(SSH1_CMSG_REQUEST_COMPRESSION);
+ translate(SSH1_CMSG_AUTH_TIS);
+ translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_TIS_RESPONSE);
+ translate(SSH1_CMSG_AUTH_CCARD);
+ translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);
+ translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
+ return "unknown";
+}
+char *ssh2_pkt_type(int type)
+{
+ translate(SSH2_MSG_DISCONNECT);
+ translate(SSH2_MSG_IGNORE);
+ translate(SSH2_MSG_UNIMPLEMENTED);
+ translate(SSH2_MSG_DEBUG);
+ translate(SSH2_MSG_SERVICE_REQUEST);
+ translate(SSH2_MSG_SERVICE_ACCEPT);
+ translate(SSH2_MSG_KEXINIT);
+ translate(SSH2_MSG_NEWKEYS);
+ translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP1);
+ translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP1);
+ translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
+ translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
+ translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
+ translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+ translate(SSH2_MSG_USERAUTH_REQUEST);
+ translate(SSH2_MSG_USERAUTH_FAILURE);
+ translate(SSH2_MSG_USERAUTH_SUCCESS);
+ translate(SSH2_MSG_USERAUTH_BANNER);
+ translatec(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
+ translatec(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
+ translatec(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
+ translatec(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
+ translate(SSH2_MSG_GLOBAL_REQUEST);
+ translate(SSH2_MSG_REQUEST_SUCCESS);
+ translate(SSH2_MSG_REQUEST_FAILURE);
+ translate(SSH2_MSG_CHANNEL_OPEN);
+ translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ translate(SSH2_MSG_CHANNEL_DATA);
+ translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);
+ translate(SSH2_MSG_CHANNEL_EOF);
+ translate(SSH2_MSG_CHANNEL_CLOSE);
+ translate(SSH2_MSG_CHANNEL_REQUEST);
+ translate(SSH2_MSG_CHANNEL_SUCCESS);
+ translate(SSH2_MSG_CHANNEL_FAILURE);
+ return "unknown";
+}
+#undef translate
+#undef translatec
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
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:
#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
static tree234 *ssh_channels; /* indexed by local id */
static struct ssh_channel *mainchan; /* primary session channel */
+static int ssh_exitcode = -1;
static tree234 *ssh_rportfwds;
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);
-#ifdef DUMP_PACKETS
- debug(("Got packet len=%d pad=%d\n", st->len, st->pad));
- dmemdump(pktin.data, st->biglen);
-#endif
st->realcrc = crc32(pktin.data, st->biglen - 4);
st->gotcrc = GET_32BIT(pktin.data + st->biglen - 4);
if (ssh1_compressing) {
unsigned char *decompblk;
int decomplen;
-#ifdef DUMP_PACKETS
- debug(("Packet payload pre-decompression:\n"));
- dmemdump(pktin.body - 1, pktin.length + 1);
-#endif
zlib_decompress_block(pktin.body - 1, pktin.length + 1,
&decompblk, &decomplen);
memcpy(pktin.body - 1, decompblk, decomplen);
sfree(decompblk);
pktin.length = decomplen - 1;
-#ifdef DUMP_PACKETS
- debug(("Packet payload post-decompression:\n"));
- dmemdump(pktin.body - 1, pktin.length + 1);
-#endif
}
+ pktin.type = pktin.body[-1];
+
+ log_packet(PKT_INCOMING, pktin.type, ssh1_pkt_type(pktin.type),
+ pktin.body, pktin.length);
+
if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
pktin.type == SSH1_SMSG_STDERR_DATA ||
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);
}
}
- pktin.type = pktin.body[-1];
-
if (pktin.type == SSH1_MSG_DEBUG) {
/* log debug message */
char buf[80];
- int strlen = GET_32BIT(pktin.body);
+ int stringlen = GET_32BIT(pktin.body);
strcpy(buf, "Remote: ");
- if (strlen > 70)
- strlen = 70;
- memcpy(buf + 8, pktin.body + 4, strlen);
- buf[8 + strlen] = '\0';
+ if (stringlen > 70)
+ stringlen = 70;
+ memcpy(buf + 8, pktin.body + 4, stringlen);
+ buf[8 + stringlen] = '\0';
logevent(buf);
goto next_packet;
} else if (pktin.type == SSH1_MSG_IGNORE) {
sccipher->decrypt(pktin.data + st->cipherblk,
st->packetlen - st->cipherblk);
-#ifdef DUMP_PACKETS
- debug(("Got packet len=%d pad=%d\n", st->len, st->pad));
- dmemdump(pktin.data, st->packetlen);
-#endif
-
/*
* Check the MAC.
*/
}
pktin.length = 5 + newlen;
memcpy(pktin.data + 5, newpayload, newlen);
-#ifdef DUMP_PACKETS
- debug(("Post-decompression payload:\n"));
- dmemdump(pktin.data + 5, newlen);
-#endif
-
sfree(newpayload);
}
}
pktin.savedpos = 6;
pktin.type = pktin.data[5];
- 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);
+ log_packet(PKT_INCOMING, pktin.type, ssh2_pkt_type(pktin.type),
+ pktin.data+6, pktin.length-6);
+
+ 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);
pktout.body[-1] = pktout.type;
+ log_packet(PKT_OUTGOING, pktout.type, ssh1_pkt_type(pktout.type),
+ pktout.body, pktout.length);
+
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);
PUT_32BIT(pktout.data + biglen, crc);
PUT_32BIT(pktout.data, len);
-#ifdef DUMP_PACKETS
- debug(("Sending packet len=%d\n", biglen + 4));
- dmemdump(pktout.data, biglen + 4);
-#endif
if (cipher)
cipher->encrypt(pktout.data + 4, biglen);
int cipherblk, maclen, padding, i;
static unsigned long outgoing_sequence = 0;
+ log_packet(PKT_OUTGOING, pktout.data[5], ssh2_pkt_type(pktout.data[5]),
+ pktout.data + 6, pktout.length - 6);
+
/*
* Compress packet payload.
*/
{
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;
outgoing_sequence);
outgoing_sequence++; /* whether or not we MACed */
-#ifdef DUMP_PACKETS
- debug(("Sending packet len=%d\n", pktout.length + padding));
- dmemdump(pktout.data, pktout.length + padding);
-#endif
-
if (cscipher)
cscipher->encrypt(pktout.data, pktout.length + padding);
logevent("We believe remote version needs a plain SSH1 password");
}
+ if (!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 (!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)) {
* 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);
- if ((err = sk_socket_error(s)))
+ s = sk_new(addr, port, 0, 1, nodelay, &fn_table_ptr);
+ if ((err = sk_socket_error(s))) {
+ s = NULL;
return err;
+ }
#ifdef FWHACK
sk_write(s, "connect ", 8);
struct RSAKey servkey, hostkey;
struct MD5Context md5c;
static unsigned long supported_ciphers_mask, supported_auths_mask;
- static int tried_publickey;
+ static int tried_publickey, tried_agent;
static int tis_auth_refused, ccard_auth_refused;
static unsigned char session_id[16];
static int cipher_type;
static char username[100];
+ static void *publickey_blob;
+ int publickey_bloblen;
crBegin;
break;
case 3:
case 4:
- random_save_seed();
- exit(0);
+ cleanup_exit(0);
break;
default:
if (((c >= ' ' && c <= '~') ||
crWaitUntil(ispkt);
- tried_publickey = 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 (!rsakey_pubblob(cfg.keyfile, &publickey_blob, &publickey_bloblen))
+ publickey_blob = NULL;
+ } else
+ publickey_blob = NULL;
while (pktin.type == SSH1_SMSG_FAILURE) {
static char password[100];
static int pwpkt_type;
pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
- if (agent_exists()) {
+ if (agent_exists() && !tried_agent) {
/*
* Attempt RSA authentication using Pageant.
*/
static int authed = FALSE;
void *r;
+ tried_agent = 1;
logevent("Pageant is running. Requesting keys.");
/* Request the keys held by the agent. */
sprintf(buf, "Trying Pageant key #%d", i);
logevent(buf);
}
+ if (publickey_blob &&
+ !memcmp(p, publickey_blob, publickey_bloblen)) {
+ logevent("This key matches configured key file");
+ tried_publickey = 1;
+ }
p += 4;
p += ssh1_read_bignum(p, &key.exponent);
p += ssh1_read_bignum(p, &key.modulus);
break;
case 3:
case 4:
- random_save_seed();
- exit(0);
+ cleanup_exit(0);
break;
default:
if (pos < sizeof(password)-1)
/* may be from EXEC_SHELL on some servers
* if no pty is available or in other odd cases. Ignore */
} else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
+ char buf[100];
+ ssh_exitcode = GET_32BIT(pktin.body);
+ sprintf(buf, "Server sent command exit status %d",
+ ssh_exitcode);
+ logevent(buf);
send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
/*
* In case `helpful' firewalls or proxies tack
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)) {
*/
if (kex == &ssh_diffiehellman_gex) {
logevent("Doing Diffie-Hellman group exchange");
+ ssh_pkt_ctx |= SSH2_PKTCTX_DHGEX;
/*
* Work out how big a DH group we will need to allow that
* much data.
kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
} else {
+ ssh_pkt_ctx |= SSH2_PKTCTX_DHGROUP1;
dh_setup_group1();
kex_init_value = SSH2_MSG_KEXDH_INIT;
kex_reply_value = SSH2_MSG_KEXDH_REPLY;
}
/*
- * 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_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.
*/
cscipher = cscipher_tobe;
*/
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 char username[100];
static char pwprompt[200];
static char password[100];
+ static void *publickey_blob;
+ static int publickey_bloblen;
crBegin;
* the username they will want to be able to get back and
* retype it!
*/
+ username[0] = '\0';
do {
static int pos;
static char c;
* Get a username.
*/
pos = 0;
- if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
+ if (*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("login as: ",
username, sizeof(username), FALSE)) {
break;
case 3:
case 4:
- random_save_seed();
- exit(0);
+ cleanup_exit(0);
break;
default:
if (((c >= ' ' && c <= '~') ||
* just in case it succeeds, and (b) so that we know what
* authentication methods we can usefully try next.
*/
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+
ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
ssh2_pkt_addstring(username);
ssh2_pkt_addstring("ssh-connection"); /* service requested */
tried_agent = FALSE;
tried_keyb_inter = FALSE;
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);
+ } else
+ publickey_blob = NULL;
while (1) {
/*
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);
}
method = 0;
+ ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
if (!method && can_pubkey && agent_exists() && !tried_agent) {
/*
static int authed = FALSE;
void *r;
+ ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+
tried_agent = TRUE;
logevent("Pageant is running. Requesting keys.");
}
pklen = GET_32BIT(p);
p += 4;
+ if (publickey_blob &&
+ pklen == publickey_bloblen &&
+ !memcmp(p, publickey_blob, publickey_bloblen)) {
+ logevent("This key matches configured key file");
+ tried_pubkey_config = 1;
+ }
pkblob = p;
p += pklen;
alglen = GET_32BIT(pkblob);
tried_pubkey_config = TRUE;
+ ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+
/*
* Try the public key supplied in the configuration.
*
type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
tried_keyb_inter = TRUE;
+ ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+
ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
ssh2_pkt_addstring(username);
ssh2_pkt_addstring("ssh-connection"); /* service requested */
type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
tried_keyb_inter = TRUE;
+ ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+
/* We've got packet with that "interactive" info
dump banners, and set its prompt as ours */
{
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();
if (!method && can_passwd) {
method = AUTH_PASSWORD;
+ ssh_pkt_ctx |= SSH2_PKTCTX_PASSWORD;
sprintf(pwprompt, "%.90s@%.90s's password: ", username,
savedhost);
need_pw = TRUE;
break;
case 3:
case 4:
- random_save_seed();
- exit(0);
+ cleanup_exit(0);
break;
default:
if (pos < sizeof(password)-1)
ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
ssh2_pkt_addstring("tcpip-forward");
ssh2_pkt_addbool(1);/* want reply */
- ssh2_pkt_addstring("127.0.0.1");
+ if (cfg.rport_acceptall)
+ ssh2_pkt_addstring("0.0.0.0");
+ else
+ ssh2_pkt_addstring("127.0.0.1");
ssh2_pkt_adduint32(sport);
ssh2_pkt_send();
* 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;
}
}
/*
- * 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.
+ * Having got the channel number, we now look at
+ * the request type string to see if it's something
+ * we recognise.
*/
- if (want_reply) {
- ssh2_pkt_init(SSH2_MSG_CHANNEL_FAILURE);
- ssh2_pkt_adduint32(c->remoteid);
- ssh2_pkt_send();
+ if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
+ c == mainchan) {
+ /* We recognise "exit-status" on the primary channel. */
+ char buf[100];
+ ssh_exitcode = ssh2_pkt_getuint32();
+ sprintf(buf, "Server sent command exit status %d",
+ ssh_exitcode);
+ logevent(buf);
+ if (want_reply) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_SUCCESS);
+ ssh2_pkt_adduint32(c->remoteid);
+ ssh2_pkt_send();
+ }
+ } else {
+ /*
+ * This is a channel request we don't know
+ * about, 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_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;
*
* 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;
return FALSE;
}
+static int ssh_return_exitcode(void)
+{
+ return ssh_exitcode;
+}
+
Backend ssh_backend = {
ssh_init,
ssh_send,
ssh_size,
ssh_special,
ssh_socket,
+ ssh_return_exitcode,
ssh_sendok,
ssh_ldisc,
ssh_unthrottle,