#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
+#include <limits.h>
+#include <signal.h>
#include "putty.h"
#include "tree234.h"
#define SSH2_MSG_KEX_DH_GEX_GROUP 31 /* 0x1f */
#define SSH2_MSG_KEX_DH_GEX_INIT 32 /* 0x20 */
#define SSH2_MSG_KEX_DH_GEX_REPLY 33 /* 0x21 */
+#define SSH2_MSG_KEXRSA_PUBKEY 30 /* 0x1e */
+#define SSH2_MSG_KEXRSA_SECRET 31 /* 0x1f */
+#define SSH2_MSG_KEXRSA_DONE 32 /* 0x20 */
#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */
*/
#define SSH2_PKTCTX_DHGROUP 0x0001
#define SSH2_PKTCTX_DHGEX 0x0002
+#define SSH2_PKTCTX_RSAKEX 0x0004
#define SSH2_PKTCTX_KEX_MASK 0x000F
#define SSH2_PKTCTX_PUBLICKEY 0x0010
#define SSH2_PKTCTX_PASSWORD 0x0020
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);
+ translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+ translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+ translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
translate(SSH2_MSG_USERAUTH_REQUEST);
translate(SSH2_MSG_USERAUTH_FAILURE);
translate(SSH2_MSG_USERAUTH_SUCCESS);
tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */
+ int ncmode; /* is primary channel direct-tcpip? */
int exitcode;
int close_expected;
int clean_exit;
{
int i;
for (i = 0; i < len; i++)
- if (buf[i] != '\r' && (trusted || buf[i] & 0x60))
+ if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
fputc(buf[i], stderr);
}
return biglen + 4; /* len(length+padding+type+data+CRC) */
}
+static int s_write(Ssh ssh, void *data, int len)
+{
+ log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+ return sk_write(ssh->s, (char *)data, len);
+}
+
static void s_wrpkt(Ssh ssh, struct Packet *pkt)
{
int len, backlog, offset;
len = s_wrpkt_prepare(ssh, pkt, &offset);
- backlog = sk_write(ssh->s, (char *)pkt->data + offset, len);
+ backlog = s_write(ssh, pkt->data + offset, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
ssh_free_packet(pkt);
while ((argtype = va_arg(ap, int)) != PKT_END) {
unsigned char *argp, argchar;
+ char *sargp;
unsigned long argint;
int arglen;
switch (argtype) {
ssh_pkt_adddata(pkt, argp, arglen);
break;
case PKT_STR:
- argp = va_arg(ap, unsigned char *);
- ssh_pkt_addstring(pkt, argp);
+ sargp = va_arg(ap, char *);
+ ssh_pkt_addstring(pkt, sargp);
break;
case PKT_BIGNUM:
bn = va_arg(ap, Bignum);
static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)
{
int len = ssh1_bignum_length(b);
- unsigned char *data = snewn(len, char);
+ unsigned char *data = snewn(len, unsigned char);
(void) ssh1_write_bignum(data, b);
ssh_pkt_adddata(pkt, data, len);
sfree(data);
return;
}
len = ssh2_pkt_construct(ssh, pkt);
- backlog = sk_write(ssh->s, (char *)pkt->data, len);
+ backlog = s_write(ssh, pkt->data, len);
if (backlog > SSH_MAX_BACKLOG)
ssh_throttle_all(ssh, 1, backlog);
* get encrypted with a known IV.
*/
struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(ipkt);
ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
}
len = ssh2_pkt_construct(ssh, pkt);
ssh2_pkt_send_noqueue(ssh, pkt);
}
-#if 0 /* disused */
/*
* Either queue or defer a packet, depending on whether queueing is
* set.
else
ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
}
-#endif
/*
* Send the whole deferred data block constructed by
static void ssh_pkt_defersend(Ssh ssh)
{
int backlog;
- backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data,
- ssh->deferred_len);
+ backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len);
ssh->deferred_len = ssh->deferred_size = 0;
sfree(ssh->deferred_send_data);
ssh->deferred_send_data = NULL;
}
/*
+ * Send a packet whose length needs to be disguised (typically
+ * passwords or keyboard-interactive responses).
+ */
+static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
+ int padsize)
+{
+#if 0
+ if (0) {
+ /*
+ * The simplest way to do this is to adjust the
+ * variable-length padding field in the outgoing packet.
+ *
+ * Currently compiled out, because some Cisco SSH servers
+ * don't like excessively padded packets (bah, why's it
+ * always Cisco?)
+ */
+ pkt->forcepad = padsize;
+ ssh2_pkt_send(ssh, pkt);
+ } else
+#endif
+ {
+ /*
+ * If we can't do that, however, an alternative approach is
+ * to use the pkt_defer mechanism to bundle the packet
+ * tightly together with an SSH_MSG_IGNORE such that their
+ * combined length is a constant. So first we construct the
+ * final form of this packet and defer its sending.
+ */
+ ssh2_pkt_defer(ssh, pkt);
+
+ /*
+ * Now construct an SSH_MSG_IGNORE which includes a string
+ * that's an exact multiple of the cipher block size. (If
+ * the cipher is NULL so that the block size is
+ * unavailable, we don't do this trick at all, because we
+ * gain nothing by it.)
+ */
+ if (ssh->cscipher) {
+ int stringlen, i;
+
+ stringlen = (256 - ssh->deferred_len);
+ stringlen += ssh->cscipher->blksize - 1;
+ stringlen -= (stringlen % ssh->cscipher->blksize);
+ if (ssh->cscomp) {
+ /*
+ * Temporarily disable actual compression, so we
+ * can guarantee to get this string exactly the
+ * length we want it. The compression-disabling
+ * routine should return an integer indicating how
+ * many bytes we should adjust our string length
+ * by.
+ */
+ stringlen -=
+ ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
+ }
+ pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start(pkt);
+ for (i = 0; i < stringlen; i++) {
+ char c = (char) random_byte();
+ ssh2_pkt_addstring_data(pkt, &c, 1);
+ }
+ ssh2_pkt_defer(ssh, pkt);
+ }
+ ssh_pkt_defersend(ssh);
+ }
+}
+
+/*
* Send all queued SSH-2 packets. We send them by means of
* ssh2_pkt_defer_noqueue(), in case they included a pair of
* packets that needed to be lumped together.
ssh->remote_bugs = 0;
+ /*
+ * General notes on server version strings:
+ * - Not all servers reporting "Cisco-1.25" have all the bugs listed
+ * here -- in particular, we've heard of one that's perfectly happy
+ * with SSH1_MSG_IGNOREs -- but this string never seems to change,
+ * so we can't distinguish them.
+ */
if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
(ssh->cfg.sshbug_ignore1 == AUTO &&
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
}
logeventf(ssh, "We claim version: %.*s",
strcspn(verstring, "\015\012"), verstring);
- sk_write(ssh->s, verstring, strlen(verstring));
+ s_write(ssh, verstring, strlen(verstring));
sfree(verstring);
if (ssh->version == 2)
do_ssh2_transport(ssh, NULL, -1, NULL);
static void ssh_process_incoming_data(Ssh ssh,
unsigned char **data, int *datalen)
{
- struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+ struct Packet *pktin;
+
+ pktin = ssh->s_rdpkt(ssh, data, datalen);
if (pktin) {
ssh->protocol(ssh, NULL, 0, pktin);
ssh_free_packet(pktin);
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
+ /* Log raw data, if we're in that mode. */
+ log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL);
+
crBegin(ssh->ssh_gotdata_crstate);
/*
error_msg = "Server closed network connection";
}
+ if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0)
+ ssh->exitcode = 0;
+
if (need_notify)
notify_remote_exit(ssh->frontend);
/* and try again */
} else {
assert(0 && "unexpected return from loadrsakey()");
+ got_passphrase = FALSE; /* placate optimisers */
}
}
* magnitude of the password length, but it will
* introduce a bit of extra uncertainty.
*
- * A few servers (the old 1.2.18 through 1.2.22)
- * can't deal with SSH1_MSG_IGNORE. For these
- * servers, we need an alternative defence. We make
- * use of the fact that the password is interpreted
- * as a C string: so we can append a NUL, then some
- * random data.
+ * A few servers can't deal with SSH1_MSG_IGNORE, at
+ * least in this context. For these servers, we need
+ * an alternative defence. We make 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
+ * A few servers can deal with neither SSH1_MSG_IGNORE
+ * here _nor_ a padded password string.
+ * For these servers we are left with no defences
* against password length sniffing.
*/
- if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&
+ !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
/*
* The server can deal with SSH1_MSG_IGNORE, so
* we can use the primary defence.
PKTT_OTHER, 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.
+ * The server is believed unable to cope with
+ * any of our password camouflage methods.
*/
int len;
len = strlen(s->cur_prompt->prompts[0]->result);
/* Data for an agent message. Buffer it. */
while (len > 0) {
if (c->u.a.lensofar < 4) {
- unsigned int l = min(4 - c->u.a.lensofar, len);
+ unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len);
memcpy(c->u.a.msglen + c->u.a.lensofar, p,
l);
p += l;
if (c->u.a.lensofar >= 4 && len > 0) {
unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
- len);
+ (unsigned)len);
memcpy(c->u.a.message + c->u.a.lensofar, p,
l);
p += l;
const struct ssh_mac *scmac_tobe;
const struct ssh_compress *cscomp_tobe;
const struct ssh_compress *sccomp_tobe;
- char *hostkeydata, *sigdata, *keystr, *fingerprint;
- int hostkeylen, siglen;
+ char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
+ int hostkeylen, siglen, rsakeylen;
void *hkey; /* actual host key */
+ void *rsakey; /* for RSA kex */
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
int n_preferred_kex;
const struct ssh_kexes *preferred_kex[KEX_MAX];
s->preferred_kex[s->n_preferred_kex++] =
&ssh_diffiehellman_group1;
break;
+ case KEX_RSA:
+ s->preferred_kex[s->n_preferred_kex++] =
+ &ssh_rsa_kex;
+ break;
case KEX_WARN:
/* Flag for later. Don't bother if it's the last in
* the list. */
crWaitUntil(pktin); /* Ignore packet */
}
- /*
- * Work out the number of bits of key we will need from the key
- * exchange. We start with the maximum key length of either
- * cipher...
- */
- {
- int csbits, scbits;
+ if (ssh->kex->main_type == KEXTYPE_DH) {
+ /*
+ * Work out the number of bits of key we will need from the
+ * key exchange. We start with the maximum key length of
+ * either cipher...
+ */
+ {
+ int csbits, scbits;
- csbits = s->cscipher_tobe->keylen;
- scbits = s->sccipher_tobe->keylen;
- s->nbits = (csbits > scbits ? csbits : scbits);
- }
- /* The keys only have hlen-bit entropy, since they're based on
- * a hash. So cap the key size at hlen bits. */
- if (s->nbits > ssh->kex->hash->hlen * 8)
- s->nbits = ssh->kex->hash->hlen * 8;
+ csbits = s->cscipher_tobe->keylen;
+ scbits = s->sccipher_tobe->keylen;
+ s->nbits = (csbits > scbits ? csbits : scbits);
+ }
+ /* The keys only have hlen-bit entropy, since they're based on
+ * a hash. So cap the key size at hlen bits. */
+ if (s->nbits > ssh->kex->hash->hlen * 8)
+ s->nbits = ssh->kex->hash->hlen * 8;
- /*
- * If we're doing Diffie-Hellman group exchange, start by
- * requesting a group.
- */
- if (!ssh->kex->pdata) {
- 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.
- */
- s->pbits = 512 << ((s->nbits - 1) / 64);
- s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
- ssh2_pkt_adduint32(s->pktout, s->pbits);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ /*
+ * If we're doing Diffie-Hellman group exchange, start by
+ * requesting a group.
+ */
+ if (!ssh->kex->pdata) {
+ 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.
+ */
+ s->pbits = 512 << ((s->nbits - 1) / 64);
+ s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
+ ssh2_pkt_adduint32(s->pktout, s->pbits);
+ ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+ crWaitUntil(pktin);
+ if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
+ bombout(("expected key exchange group packet from server"));
+ crStop(0);
+ }
+ s->p = ssh2_pkt_getmp(pktin);
+ s->g = ssh2_pkt_getmp(pktin);
+ if (!s->p || !s->g) {
+ bombout(("unable to read mp-ints from incoming group packet"));
+ crStop(0);
+ }
+ ssh->kex_ctx = dh_setup_gex(s->p, s->g);
+ s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
+ s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+ } else {
+ ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP;
+ ssh->kex_ctx = dh_setup_group(ssh->kex);
+ s->kex_init_value = SSH2_MSG_KEXDH_INIT;
+ s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
+ logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
+ ssh->kex->groupname);
+ }
- crWaitUntil(pktin);
- if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
- bombout(("expected key exchange group packet from server"));
- crStop(0);
- }
- s->p = ssh2_pkt_getmp(pktin);
- s->g = ssh2_pkt_getmp(pktin);
- if (!s->p || !s->g) {
- bombout(("unable to read mp-ints from incoming group packet"));
- crStop(0);
- }
- ssh->kex_ctx = dh_setup_gex(s->p, s->g);
- s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
- s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+ logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
+ ssh->kex->hash->text_name);
+ /*
+ * Now generate and send e for Diffie-Hellman.
+ */
+ set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
+ s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
+ s->pktout = ssh2_pkt_init(s->kex_init_value);
+ ssh2_pkt_addmp(s->pktout, s->e);
+ ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+ set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
+ crWaitUntil(pktin);
+ if (pktin->type != s->kex_reply_value) {
+ bombout(("expected key exchange reply packet from server"));
+ crStop(0);
+ }
+ set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
+ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+ s->f = ssh2_pkt_getmp(pktin);
+ if (!s->f) {
+ bombout(("unable to parse key exchange reply packet"));
+ crStop(0);
+ }
+ ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+ s->K = dh_find_K(ssh->kex_ctx, s->f);
+
+ /* We assume everything from now on will be quick, and it might
+ * involve user interaction. */
+ set_busy_status(ssh->frontend, BUSY_NOT);
+
+ hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
+ if (!ssh->kex->pdata) {
+ hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+ }
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
+ hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
+ dh_cleanup(ssh->kex_ctx);
+ freebn(s->f);
+ if (!ssh->kex->pdata) {
+ freebn(s->g);
+ freebn(s->p);
+ }
} else {
- ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP;
- ssh->kex_ctx = dh_setup_group(ssh->kex);
- s->kex_init_value = SSH2_MSG_KEXDH_INIT;
- s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
- logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
- ssh->kex->groupname);
- }
+ logeventf(ssh, "Doing RSA key exchange with hash %s",
+ ssh->kex->hash->text_name);
+ ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX;
+ /*
+ * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+ * from the server.
+ */
+ crWaitUntil(pktin);
+ if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+ bombout(("expected RSA public key packet from server"));
+ crStop(0);
+ }
- logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
- ssh->kex->hash->text_name);
- /*
- * Now generate and send e for Diffie-Hellman.
- */
- set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
- s->e = dh_create_e(ssh->kex_ctx, s->nbits * 2);
- s->pktout = ssh2_pkt_init(s->kex_init_value);
- ssh2_pkt_addmp(s->pktout, s->e);
- ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+ hash_string(ssh->kex->hash, ssh->exhash,
+ s->hostkeydata, s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
- set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
- crWaitUntil(pktin);
- if (pktin->type != s->kex_reply_value) {
- bombout(("expected key exchange reply packet from server"));
- crStop(0);
- }
- set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
- ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
- s->f = ssh2_pkt_getmp(pktin);
- if (!s->f) {
- bombout(("unable to parse key exchange reply packet"));
- crStop(0);
- }
- ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+ {
+ char *keydata;
+ ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+ s->rsakeydata = snewn(s->rsakeylen, char);
+ memcpy(s->rsakeydata, keydata, s->rsakeylen);
+ }
- s->K = dh_find_K(ssh->kex_ctx, s->f);
+ s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+ if (!s->rsakey) {
+ sfree(s->rsakeydata);
+ bombout(("unable to parse RSA public key from server"));
+ crStop(0);
+ }
- /* We assume everything from now on will be quick, and it might
- * involve user interaction. */
- set_busy_status(ssh->frontend, BUSY_NOT);
+ hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
- hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
- if (!ssh->kex->pdata) {
- hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
- hash_mpint(ssh->kex->hash, ssh->exhash, s->p);
- hash_mpint(ssh->kex->hash, ssh->exhash, s->g);
+ /*
+ * Next, set up a shared secret K, of precisely KLEN -
+ * 2*HLEN - 49 bits, where KLEN is the bit length of the
+ * RSA key modulus and HLEN is the bit length of the hash
+ * we're using.
+ */
+ {
+ int klen = ssh_rsakex_klen(s->rsakey);
+ int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
+ int i, byte = 0;
+ unsigned char *kstr1, *kstr2, *outstr;
+ int kstr1len, kstr2len, outstrlen;
+
+ s->K = bn_power_2(nbits - 1);
+
+ for (i = 0; i < nbits; i++) {
+ if ((i & 7) == 0) {
+ byte = random_byte();
+ }
+ bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
+ }
+
+ /*
+ * Encode this as an mpint.
+ */
+ kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
+ kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
+ PUT_32BIT(kstr2, kstr1len);
+ memcpy(kstr2 + 4, kstr1, kstr1len);
+
+ /*
+ * Encrypt it with the given RSA key.
+ */
+ outstrlen = (klen + 7) / 8;
+ outstr = snewn(outstrlen, unsigned char);
+ ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
+ outstr, outstrlen, s->rsakey);
+
+ /*
+ * And send it off in a return packet.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+ ssh2_pkt_addstring_start(s->pktout);
+ ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen);
+ ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+ hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
+
+ sfree(kstr2);
+ sfree(kstr1);
+ sfree(outstr);
+ }
+
+ ssh_rsakex_freekey(s->rsakey);
+
+ crWaitUntil(pktin);
+ if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+ sfree(s->rsakeydata);
+ bombout(("expected signature packet from server"));
+ crStop(0);
+ }
+
+ ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+ sfree(s->rsakeydata);
}
- hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
- hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
- dh_cleanup(ssh->kex_ctx);
ssh->kex_ctx = NULL;
#if 0
dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
#endif
- s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
if (!s->hkey ||
!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
(char *)s->exchange_hash,
ssh->sccomp->text_name);
/*
- * Free key exchange data.
+ * Free shared secret.
*/
- freebn(s->f);
freebn(s->K);
- if (!ssh->kex->pdata) {
- freebn(s->g);
- freebn(s->p);
- }
/*
* Key exchange is over. Loop straight back round if we have a
case CHAN_AGENT:
while (length > 0) {
if (c->u.a.lensofar < 4) {
- unsigned int l = min(4 - c->u.a.lensofar, length);
+ unsigned int l = min(4 - c->u.a.lensofar,
+ (unsigned)length);
memcpy(c->u.a.msglen + c->u.a.lensofar,
data, l);
data += l;
if (c->u.a.lensofar >= 4 && length > 0) {
unsigned int l =
min(c->u.a.totallen - c->u.a.lensofar,
- length);
+ (unsigned)length);
memcpy(c->u.a.message + c->u.a.lensofar,
data, l);
data += l;
is_plausible = FALSE;
}
}
+ ssh->exitcode = 128; /* means `unknown signal' */
if (is_plausible) {
if (is_int) {
/* Old non-standard OpenSSH. */
int signum = ssh_pkt_getuint32(pktin);
fmt_sig = dupprintf(" %d", signum);
+ ssh->exitcode = 128 + signum;
} else {
/* As per the drafts. */
char *sig;
fmt_sig = dupprintf(" \"%.*s\"",
siglen, sig);
}
+
+ /*
+ * Really hideous method of translating the
+ * signal description back into a locally
+ * meaningful number.
+ */
+
+ if (0)
+ ;
+#define TRANSLATE_SIGNAL(s) \
+ else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
+ ssh->exitcode = 128 + SIG ## s
+#ifdef SIGABRT
+ TRANSLATE_SIGNAL(ABRT);
+#endif
+#ifdef SIGALRM
+ TRANSLATE_SIGNAL(ALRM);
+#endif
+#ifdef SIGFPE
+ TRANSLATE_SIGNAL(FPE);
+#endif
+#ifdef SIGHUP
+ TRANSLATE_SIGNAL(HUP);
+#endif
+#ifdef SIGILL
+ TRANSLATE_SIGNAL(ILL);
+#endif
+#ifdef SIGINT
+ TRANSLATE_SIGNAL(INT);
+#endif
+#ifdef SIGKILL
+ TRANSLATE_SIGNAL(KILL);
+#endif
+#ifdef SIGPIPE
+ TRANSLATE_SIGNAL(PIPE);
+#endif
+#ifdef SIGQUIT
+ TRANSLATE_SIGNAL(QUIT);
+#endif
+#ifdef SIGSEGV
+ TRANSLATE_SIGNAL(SEGV);
+#endif
+#ifdef SIGTERM
+ TRANSLATE_SIGNAL(TERM);
+#endif
+#ifdef SIGUSR1
+ TRANSLATE_SIGNAL(USR1);
+#endif
+#ifdef SIGUSR2
+ TRANSLATE_SIGNAL(USR2);
+#endif
+#undef TRANSLATE_SIGNAL
+ else
+ ssh->exitcode = 128;
}
core = ssh2_pkt_getbool(pktin);
ssh_pkt_getstring(pktin, &msg, &msglen);
* Send the responses to the server.
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
- s->pktout->forcepad = 256;
ssh2_pkt_adduint32(s->pktout, s->num_prompts);
for (i=0; i < s->num_prompts; i++) {
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
s->cur_prompt->prompts[i]->result);
end_log_omission(ssh, s->pktout);
}
- ssh2_pkt_send(ssh, s->pktout);
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
/*
* Get the next packet in case it's another
* people who find out how long their password is!
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- s->pktout->forcepad = 256;
ssh2_pkt_addstring(s->pktout, s->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, s->password);
end_log_omission(ssh, s->pktout);
- ssh2_pkt_send(ssh, s->pktout);
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
logevent("Sent password");
s->type = AUTH_TYPE_PASSWORD;
s->cur_prompt->instruction =
dupprintf("%.*s", prompt_len, prompt);
s->cur_prompt->instr_reqd = TRUE;
+ /*
+ * There's no explicit requirement in the protocol
+ * for the "old" passwords in the original and
+ * password-change messages to be the same, and
+ * apparently some Cisco kit supports password change
+ * by the user entering a blank password originally
+ * and the real password subsequently, so,
+ * reluctantly, we prompt for the old password again.
+ *
+ * (On the other hand, some servers don't even bother
+ * to check this field.)
+ */
+ add_prompt(s->cur_prompt,
+ dupstr("Current password (blank for previously entered password): "),
+ FALSE, SSH_MAX_PASSWORD_LEN);
add_prompt(s->cur_prompt, dupstr("Enter new password: "),
FALSE, SSH_MAX_PASSWORD_LEN);
add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
}
/*
- * Check the two passwords match.
+ * If the user specified a new original password
+ * (IYSWIM), overwrite any previously specified
+ * one.
+ * (A side effect is that the user doesn't have to
+ * re-enter it if they louse up the new password.)
*/
- got_new = (strcmp(s->cur_prompt->prompts[0]->result,
- s->cur_prompt->prompts[1]->result)
+ if (s->cur_prompt->prompts[0]->result[0]) {
+ memset(s->password, 0, strlen(s->password));
+ /* burn the evidence */
+ sfree(s->password);
+ s->password =
+ dupstr(s->cur_prompt->prompts[0]->result);
+ }
+
+ /*
+ * Check the two new passwords match.
+ */
+ got_new = (strcmp(s->cur_prompt->prompts[1]->result,
+ s->cur_prompt->prompts[2]->result)
== 0);
if (!got_new)
/* They don't. Silly user. */
* (see above for padding rationale)
*/
s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
- s->pktout->forcepad = 256;
ssh2_pkt_addstring(s->pktout, s->username);
ssh2_pkt_addstring(s->pktout, "ssh-connection");
/* service requested */
dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
ssh2_pkt_addstring(s->pktout, s->password);
ssh2_pkt_addstring(s->pktout,
- s->cur_prompt->prompts[0]->result);
+ s->cur_prompt->prompts[1]->result);
free_prompts(s->cur_prompt);
end_log_omission(ssh, s->pktout);
- ssh2_pkt_send(ssh, s->pktout);
+ ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
logevent("Sent new password");
/*
/*
* Create the main session channel.
*/
- if (!ssh->cfg.ssh_no_shell) {
+ if (ssh->cfg.ssh_no_shell) {
+ ssh->mainchan = NULL;
+ } else if (*ssh->cfg.ssh_nc_host) {
+ /*
+ * Just start a direct-tcpip channel and use it as the main
+ * channel.
+ */
+ ssh->mainchan = snew(struct ssh_channel);
+ ssh->mainchan->ssh = ssh;
+ ssh->mainchan->localid = alloc_channel_id(ssh);
+ logeventf(ssh,
+ "Opening direct-tcpip channel to %s:%d in place of session",
+ ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);
+ s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+ ssh2_pkt_addstring(s->pktout, "direct-tcpip");
+ ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
+ ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
+ ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
+ ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT); /* our max pkt size */
+ ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
+ ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+ /*
+ * There's nothing meaningful to put in the originator
+ * fields, but some servers insist on syntactically correct
+ * information.
+ */
+ ssh2_pkt_addstring(s->pktout, "0.0.0.0");
+ ssh2_pkt_adduint32(s->pktout, 0);
+ ssh2_pkt_send(ssh, s->pktout);
+
+ crWaitUntilV(pktin);
+ if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ bombout(("Server refused to open a direct-tcpip channel"));
+ crStopV;
+ /* FIXME: error data comes back in FAILURE packet */
+ }
+ if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ crStopV;
+ }
+ ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->halfopen = FALSE;
+ ssh->mainchan->type = CHAN_MAINSESSION;
+ ssh->mainchan->closes = 0;
+ ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
+ ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+ bufchain_init(&ssh->mainchan->v.v2.outbuffer);
+ add234(ssh->channels, ssh->mainchan);
+ update_specials_menu(ssh->frontend);
+ logevent("Opened direct-tcpip channel");
+ ssh->ncmode = TRUE;
+ } else {
ssh->mainchan = snew(struct ssh_channel);
ssh->mainchan->ssh = ssh;
ssh->mainchan->localid = alloc_channel_id(ssh);
add234(ssh->channels, ssh->mainchan);
update_specials_menu(ssh->frontend);
logevent("Opened channel for session");
- } else
- ssh->mainchan = NULL;
+ ssh->ncmode = FALSE;
+ }
/*
* Now we have a channel, make dispatch table entries for
/*
* Potentially enable X11 forwarding.
*/
- if (ssh->mainchan && ssh->cfg.x11_forward) {
+ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
char proto[20], data[64];
logevent("Requesting X11 forwarding");
ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
/*
* Potentially enable agent forwarding.
*/
- if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {
+ if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
logevent("Requesting OpenSSH-style agent forwarding");
s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
/*
* Now allocate a pty for the session.
*/
- if (ssh->mainchan && !ssh->cfg.nopty) {
+ if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
/* Unpick the terminal-speed string. */
/* XXX perhaps we should allow no speeds to be sent. */
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
* Simplest thing here is to send all the requests at once, and
* then wait for a whole bunch of successes or failures.
*/
- if (ssh->mainchan && *ssh->cfg.environmt) {
+ if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {
char *e = ssh->cfg.environmt;
char *var, *varend, *val;
* this twice if the config data has provided a second choice
* of command.
*/
- if (ssh->mainchan) while (1) {
+ if (ssh->mainchan && !ssh->ncmode) while (1) {
int subsys;
char *cmd;
ssh1_throttle(ssh, -1);
}
} else {
- if (ssh->mainchan && ssh->mainchan->closes == 0)
- ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
+ ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
}
}
}
}
-static Socket ssh_socket(void *handle)
+static int ssh_connected(void *handle)
{
Ssh ssh = (Ssh) handle;
- return ssh->s;
+ return ssh->s != NULL;
}
static int ssh_sendok(void *handle)
if (ssh->s != NULL)
return -1;
else
- return (ssh->exitcode >= 0 ? ssh->exitcode : 0);
+ return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);
}
/*
ssh_size,
ssh_special,
ssh_get_specials,
- ssh_socket,
+ ssh_connected,
ssh_return_exitcode,
ssh_sendok,
ssh_ldisc,