if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
fprintf(stderr, "%s\n", s); }
-#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, sk_close(s), \
- s = NULL, connection_fatal msg )
+#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
+ (s ? sk_close(s), s = NULL : 0), \
+ connection_fatal msg )
#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
#define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */
#define SSH2_MSG_NEWKEYS 21 /* 0x15 */
#define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */
#define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */
+#define SSH2_MSG_KEX_DH_GEX_REQUEST 30 /* 0x1e */
+#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_USERAUTH_REQUEST 50 /* 0x32 */
#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */
#define crWaitUntilV(c) do { crReturnV; } while (!(c))
extern const struct ssh_cipher ssh_3des;
-extern const struct ssh_cipher ssh_3des_ssh2;
+extern const struct ssh2_ciphers ssh2_3des;
extern const struct ssh_cipher ssh_des;
+extern const struct ssh2_ciphers ssh2_aes;
extern const struct ssh_cipher ssh_blowfish_ssh1;
-extern const struct ssh_cipher ssh_blowfish_ssh2;
+extern const struct ssh2_ciphers ssh2_blowfish;
-extern char *x11_init (Socket *, char *, void *, char **);
+extern char *x11_init (Socket *, char *, void *);
extern void x11_close (Socket);
extern void x11_send (Socket , char *, int);
extern void x11_invent_auth(char *, int, char *, int);
* SSH1. (3DES uses outer chaining; Blowfish has the opposite
* endianness and different-sized keys.)
*/
-const static struct ssh_cipher *ciphers[] = { &ssh_blowfish_ssh2, &ssh_3des_ssh2 };
+const static struct ssh2_ciphers *ciphers[] = {
+ &ssh2_aes,
+ &ssh2_blowfish,
+ &ssh2_3des,
+};
extern const struct ssh_kex ssh_diffiehellman;
-const static struct ssh_kex *kex_algs[] = { &ssh_diffiehellman };
+extern const struct ssh_kex ssh_diffiehellman_gex;
+const static struct ssh_kex *kex_algs[] = {
+#ifdef DO_DIFFIE_HELLMAN_GEX
+ &ssh_diffiehellman_gex,
+#endif
+ &ssh_diffiehellman };
extern const struct ssh_signkey ssh_dss;
const static struct ssh_signkey *hostkey_algs[] = { &ssh_dss };
static int ssh_agentfwd_enabled;
static int ssh_X11_fwd_enabled;
static const struct ssh_cipher *cipher = NULL;
-static const struct ssh_cipher *cscipher = NULL;
-static const struct ssh_cipher *sccipher = NULL;
+static const struct ssh2_cipher *cscipher = NULL;
+static const struct ssh2_cipher *sccipher = NULL;
static const struct ssh_mac *csmac = NULL;
static const struct ssh_mac *scmac = NULL;
static const struct ssh_compress *cscomp = NULL;
static char *savedhost;
static int savedport;
static int ssh_send_ok;
+static int ssh_echoing, ssh_editing;
static tree234 *ssh_channels; /* indexed by local id */
static struct ssh_channel *mainchan; /* primary session channel */
static struct Packet pktin = { 0, 0, NULL, NULL, 0 };
static struct Packet pktout = { 0, 0, NULL, NULL, 0 };
+static unsigned char *deferred_send_data = NULL;
+static int deferred_len = 0, deferred_size = 0;
static int ssh_version;
static void (*ssh_protocol)(unsigned char *in, int inlen, int ispkt);
/*
- * Utility routine for putting an SSH-protocol `string' into a SHA
- * state.
+ * Utility routines for putting an SSH-protocol `string' and
+ * `uint32' into a SHA state.
*/
#include <stdio.h>
static void sha_string(SHA_State *s, void *str, int len) {
SHA_Bytes(s, str, len);
}
+static void sha_uint32(SHA_State *s, unsigned i) {
+ unsigned char intblk[4];
+ PUT_32BIT(intblk, i);
+ SHA_Bytes(s, intblk, 4);
+}
+
/*
* SSH2 packet construction functions.
*/
}
static char *ssh2_mpint_fmt(Bignum b, int *len) {
unsigned char *p;
- int i, n = b[0];
- p = smalloc(n * 2 + 1);
+ int i, n = (ssh1_bignum_bitcount(b)+7)/8;
+ p = smalloc(n + 1);
if (!p)
fatalbox("out of memory");
p[0] = 0;
- for (i = 0; i < n; i++) {
- p[i*2+1] = (b[n-i] >> 8) & 0xFF;
- p[i*2+2] = (b[n-i] ) & 0xFF;
- }
+ for (i = 1; i <= n; i++)
+ p[i] = bignum_byte(b, n-i);
i = 0;
- while (p[i] == 0 && (p[i+1] & 0x80) == 0)
+ while (i <= n && p[i] == 0 && (p[i+1] & 0x80) == 0)
i++;
- memmove(p, p+i, n*2+1-i);
- *len = n*2+1-i;
+ memmove(p, p+i, n+1-i);
+ *len = n+1-i;
return p;
}
static void ssh2_pkt_addmp(Bignum b) {
ssh2_pkt_addstring_data(p, len);
sfree(p);
}
-static void ssh2_pkt_send(void) {
+
+/*
+ * Construct an SSH2 final-form packet: compress it, encrypt it,
+ * put the MAC on it. Final packet, ready to be sent, is stored in
+ * pktout.data. Total length is returned.
+ */
+static int ssh2_pkt_construct(void) {
int cipherblk, maclen, padding, i;
static unsigned long outgoing_sequence = 0;
* Add padding. At least four bytes, and must also bring total
* length (minus MAC) up to a multiple of the block size.
*/
- cipherblk = cipher ? cipher->blksize : 8; /* block size */
+ cipherblk = cscipher ? cscipher->blksize : 8; /* block size */
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
padding = 4;
padding += (cipherblk - (pktout.length + padding) % cipherblk) % cipherblk;
if (cscipher)
cscipher->encrypt(pktout.data, pktout.length + padding);
- sk_write(s, pktout.data, pktout.length + padding + maclen);
+ /* Ready-to-send packet starts at pktout.data. We return length. */
+ return pktout.length + padding + maclen;
+}
+
+/*
+ * Construct and send an SSH2 packet immediately.
+ */
+static void ssh2_pkt_send(void) {
+ int len = ssh2_pkt_construct();
+ sk_write(s, pktout.data, len);
+}
+
+/*
+ * Construct an SSH2 packet and add it to a deferred data block.
+ * Useful for sending multiple packets in a single sk_write() call,
+ * to prevent a traffic-analysing listener from being able to work
+ * out the length of any particular packet (such as the password
+ * packet).
+ *
+ * Note that because SSH2 sequence-numbers its packets, this can
+ * NOT be used as an m4-style `defer' allowing packets to be
+ * constructed in one order and sent in another.
+ */
+static void ssh2_pkt_defer(void) {
+ int len = ssh2_pkt_construct();
+ if (deferred_len + len > deferred_size) {
+ deferred_size = deferred_len + len + 128;
+ deferred_send_data = srealloc(deferred_send_data, deferred_size);
+ }
+ memcpy(deferred_send_data+deferred_len, pktout.data, len);
+ deferred_len += len;
+}
+
+/*
+ * Send the whole deferred data block constructed by
+ * ssh2_pkt_defer().
+ */
+static void ssh2_pkt_defersend(void) {
+ sk_write(s, deferred_send_data, deferred_len);
+ deferred_len = deferred_size = 0;
+ sfree(deferred_send_data);
+ deferred_send_data = NULL;
}
#if 0
}
static Bignum ssh2_pkt_getmp(void) {
char *p;
- int i, j, length;
+ int length;
Bignum b;
ssh2_pkt_getstring(&p, &length);
bombout(("internal error: Can't handle negative mpints"));
return NULL;
}
- b = newbn((length+1)/2);
- for (i = 0; i < length; i++) {
- j = length - 1 - i;
- if (j & 1)
- b[j/2+1] |= ((unsigned char)p[i]) << 8;
- else
- b[j/2+1] |= ((unsigned char)p[i]);
- }
- while (b[0] > 1 && b[b[0]] == 0) b[0]--;
+ b = bignum_from_bytes(p, length);
return b;
}
}
static int ssh_receive(Socket skt, int urgent, char *data, int len) {
- if (!len) {
+ if (urgent==3) {
+ /* A socket error has occurred. */
+ ssh_state = SSH_STATE_CLOSED;
+ sk_close(s);
+ s = NULL;
+ connection_fatal(data);
+ return 0;
+ } else if (!len) {
/* Connection has closed. */
ssh_state = SSH_STATE_CLOSED;
sk_close(s);
/*
* Open socket.
*/
- s = sk_new(addr, port, 0, ssh_receive);
+ s = sk_new(addr, port, 0, 1, ssh_receive);
if ( (err = sk_socket_error(s)) )
return err;
logevent("Encrypted session key");
- cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
- cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES :
- SSH_CIPHER_3DES;
+ 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("AES not supported in SSH1, falling back to 3DES\r\n", 49);
+ cipher_type = SSH_CIPHER_3DES;
+ break;
+ }
if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
c_write("Selected cipher not supported, falling back to 3DES\r\n", 53);
cipher_type = SSH_CIPHER_3DES;
response = rsadecrypt(challenge, &pubkey);
freebn(pubkey.private_exponent); /* burn the evidence */
- for (i = 0; i < 32; i += 2) {
- buffer[i] = response[16-i/2] >> 8;
- buffer[i+1] = response[16-i/2] & 0xFF;
+ for (i = 0; i < 32; i++) {
+ buffer[i] = bignum_byte(response, 31-i);
}
MD5Init(&md5c);
crReturnV;
} else if (pktin.type == SSH1_SMSG_FAILURE) {
c_write("Server refused to allocate pty\r\n", 32);
+ ssh_editing = ssh_echoing = 1;
}
logevent("Allocated pty");
+ } else {
+ ssh_editing = ssh_echoing = 1;
}
if (cfg.compression) {
if (eof_needed)
ssh_special(TS_EOF);
+ ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1;
ssh_channels = newtree234(ssh_channelcmp);
- begin_session();
while (1) {
crReturnV;
if (ispkt) {
PKT_END);
logevent("Rejected X11 connect request");
} else {
- char *rh;
-
c = smalloc(sizeof(struct ssh_channel));
- if ( x11_init(&c->u.x11.s, cfg.x11_display, c, &rh) != NULL ) {
+ if ( x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL ) {
logevent("opening X11 forward connection failed");
sfree(c);
send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
/*
* SSH2 key creation method.
*/
-static void ssh2_mkkey(Bignum K, char *H, char chr, char *keyspace) {
+static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace) {
SHA_State s;
/* First 20 bytes. */
SHA_Init(&s);
sha_mpint(&s, K);
SHA_Bytes(&s, H, 20);
SHA_Bytes(&s, &chr, 1);
- SHA_Bytes(&s, H, 20);
+ SHA_Bytes(&s, sessid, 20);
SHA_Final(&s, keyspace);
/* Next 20 bytes. */
SHA_Init(&s);
*/
static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
{
- static int i, len;
+ static int i, j, len, nbits;
static char *str;
- static Bignum e, f, K;
+ static Bignum p, g, e, f, K;
+ static int kex_init_value, kex_reply_value;
static const struct ssh_mac **maclist;
static int nmacs;
- static const struct ssh_cipher *cscipher_tobe = NULL;
- static const struct ssh_cipher *sccipher_tobe = NULL;
+ static const struct ssh2_cipher *cscipher_tobe = NULL;
+ static const struct ssh2_cipher *sccipher_tobe = NULL;
static const struct ssh_mac *csmac_tobe = NULL;
static const struct ssh_mac *scmac_tobe = NULL;
static const struct ssh_compress *cscomp_tobe = NULL;
static int hostkeylen, siglen;
static void *hkey; /* actual host key */
static unsigned char exchange_hash[20];
+ static unsigned char first_exchange_hash[20];
static unsigned char keyspace[40];
- static const struct ssh_cipher *preferred_cipher;
+ static const struct ssh2_ciphers *preferred_cipher;
static const struct ssh_compress *preferred_comp;
static int first_kex;
* Set up the preferred cipher and compression.
*/
if (cfg.cipher == CIPHER_BLOWFISH) {
- preferred_cipher = &ssh_blowfish_ssh2;
+ preferred_cipher = &ssh2_blowfish;
} else if (cfg.cipher == CIPHER_DES) {
logevent("Single DES not supported in SSH2; using 3DES");
- preferred_cipher = &ssh_3des_ssh2;
+ preferred_cipher = &ssh2_3des;
} else if (cfg.cipher == CIPHER_3DES) {
- preferred_cipher = &ssh_3des_ssh2;
+ 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 = &ssh_3des_ssh2;
+ preferred_cipher = &ssh2_3des;
}
if (cfg.compression)
preferred_comp = &ssh_zlib;
/* List client->server encryption algorithms. */
ssh2_pkt_addstring_start();
for (i = 0; i < lenof(ciphers)+1; i++) {
- const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
- ssh2_pkt_addstring_str(c->name);
- if (i < lenof(ciphers))
- ssh2_pkt_addstring_str(",");
+ const struct ssh2_ciphers *c = i==0 ? preferred_cipher : ciphers[i-1];
+ for (j = 0; j < c->nciphers; j++) {
+ ssh2_pkt_addstring_str(c->list[j]->name);
+ if (i < lenof(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 ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
- ssh2_pkt_addstring_str(c->name);
- if (i < lenof(ciphers))
- ssh2_pkt_addstring_str(",");
+ const struct ssh2_ciphers *c = i==0 ? preferred_cipher : ciphers[i-1];
+ for (j = 0; j < c->nciphers; j++) {
+ ssh2_pkt_addstring_str(c->list[j]->name);
+ if (i < lenof(ciphers) || j < c->nciphers-1)
+ ssh2_pkt_addstring_str(",");
+ }
}
/* List client->server MAC algorithms. */
ssh2_pkt_addstring_start();
}
ssh2_pkt_getstring(&str, &len); /* client->server cipher */
for (i = 0; i < lenof(ciphers)+1; i++) {
- const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
- if (in_commasep_string(c->name, str, len)) {
- cscipher_tobe = c;
- break;
+ 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;
+ }
}
+ if (cscipher_tobe)
+ break;
}
ssh2_pkt_getstring(&str, &len); /* server->client cipher */
for (i = 0; i < lenof(ciphers)+1; i++) {
- const struct ssh_cipher *c = i==0 ? preferred_cipher : ciphers[i-1];
- if (in_commasep_string(c->name, str, len)) {
- sccipher_tobe = c;
- break;
+ 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;
+ }
}
+ if (sccipher_tobe)
+ break;
}
ssh2_pkt_getstring(&str, &len); /* client->server mac */
for (i = 0; i < nmacs; i++) {
}
/*
- * Currently we only support Diffie-Hellman and DSS, so let's
- * bomb out if those aren't selected.
+ * If we're doing Diffie-Hellman group exchange, start by
+ * requesting a group.
*/
- if (kex != &ssh_diffiehellman || hostkey != &ssh_dss) {
- bombout(("internal fault: chaos in SSH 2 transport layer"));
- crReturn(0);
+ if (kex == &ssh_diffiehellman_gex) {
+ int csbits, scbits;
+
+ logevent("Doing Diffie-Hellman group exchange");
+ /*
+ * Work out number of bits. We start with the maximum key
+ * length of either cipher...
+ */
+ csbits = cscipher_tobe->keylen;
+ scbits = sccipher_tobe->keylen;
+ nbits = (csbits > scbits ? csbits : scbits);
+ /* The keys only have 160-bit entropy, since they're based on
+ * a SHA-1 hash. So cap the key size at 160 bits. */
+ if (nbits > 160) nbits = 160;
+ /*
+ * ... and then work out how big a DH group we will need to
+ * allow that much data.
+ */
+ nbits = 512 << ((nbits-1) / 64);
+ ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
+ ssh2_pkt_adduint32(nbits);
+ ssh2_pkt_send();
+
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH2_MSG_KEX_DH_GEX_GROUP) {
+ bombout(("expected key exchange group packet from server"));
+ crReturn(0);
+ }
+ p = ssh2_pkt_getmp();
+ g = ssh2_pkt_getmp();
+ dh_setup_group(p, g);
+ kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
+ kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+ } else {
+ dh_setup_group1();
+ kex_init_value = SSH2_MSG_KEXDH_INIT;
+ kex_reply_value = SSH2_MSG_KEXDH_REPLY;
}
+ logevent("Doing Diffie-Hellman key exchange");
/*
- * Now we begin the fun. Generate and send e for Diffie-Hellman.
+ * Now generate and send e for Diffie-Hellman.
*/
e = dh_create_e();
- ssh2_pkt_init(SSH2_MSG_KEXDH_INIT);
+ ssh2_pkt_init(kex_init_value);
ssh2_pkt_addmp(e);
ssh2_pkt_send();
crWaitUntil(ispkt);
- if (pktin.type != SSH2_MSG_KEXDH_REPLY) {
- bombout(("expected key exchange packet from server"));
+ if (pktin.type != kex_reply_value) {
+ bombout(("expected key exchange reply packet from server"));
crReturn(0);
}
ssh2_pkt_getstring(&hostkeydata, &hostkeylen);
K = dh_find_K(f);
sha_string(&exhash, hostkeydata, hostkeylen);
+ if (kex == &ssh_diffiehellman_gex) {
+ sha_uint32(&exhash, nbits);
+ sha_mpint(&exhash, p);
+ sha_mpint(&exhash, g);
+ }
sha_mpint(&exhash, e);
sha_mpint(&exhash, f);
sha_mpint(&exhash, K);
SHA_Final(&exhash, exchange_hash);
+ dh_cleanup();
+
#if 0
debug(("Exchange hash is:\r\n"));
for (i = 0; i < 20; i++)
fingerprint = hostkey->fingerprint(hkey);
verify_ssh_host_key(savedhost, savedport, hostkey->keytype,
keystr, fingerprint);
- logevent("Host key fingerprint is:");
- logevent(fingerprint);
+ if (first_kex) { /* don't bother logging this in rekeys */
+ logevent("Host key fingerprint is:");
+ logevent(fingerprint);
+ }
sfree(fingerprint);
sfree(keystr);
hostkey->freekey(hkey);
cscomp->compress_init();
sccomp->decompress_init();
/*
- * Set IVs after keys.
+ * Set IVs after keys. Here we use the exchange hash from the
+ * _first_ key exchange.
*/
- ssh2_mkkey(K, exchange_hash, 'C', keyspace); cscipher->setcskey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'D', keyspace); sccipher->setsckey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'A', keyspace); cscipher->setcsiv(keyspace);
- ssh2_mkkey(K, exchange_hash, 'B', keyspace); sccipher->setsciv(keyspace);
- ssh2_mkkey(K, exchange_hash, 'E', keyspace); csmac->setcskey(keyspace);
- ssh2_mkkey(K, exchange_hash, 'F', keyspace); scmac->setsckey(keyspace);
+ if (first_kex)
+ memcpy(first_exchange_hash, exchange_hash, sizeof(exchange_hash));
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'C', keyspace);
+ cscipher->setcskey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'D', keyspace);
+ sccipher->setsckey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'A', keyspace);
+ cscipher->setcsiv(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'B', keyspace);
+ sccipher->setsciv(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'E', keyspace);
+ csmac->setcskey(keyspace);
+ ssh2_mkkey(K, exchange_hash, first_exchange_hash, 'F', keyspace);
+ scmac->setsckey(keyspace);
/*
* If this is the first key exchange phase, we must pass the
* transport. If we ever see a KEXINIT, we must go back to the
* start.
*/
- do {
+ while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)) {
crReturn(1);
- } while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT));
+ }
+ logevent("Server initiated key re-exchange");
goto begin_key_exchange;
crFinish(1);
c_write("\r\n", 2);
}
+ /*
+ * We send the password packet lumped tightly together with
+ * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
+ * string long enough to make the total length of the two
+ * packets constant. This should ensure that a passive
+ * listener doing traffic analyis can't work out the length
+ * of the password.
+ *
+ * For this to work, we need an assumption about the
+ * maximum length of the password packet. I think 256 is
+ * pretty conservative. Anyone using a password longer than
+ * that probably doesn't have much to worry about from
+ * people who find out how long their password is!
+ */
ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
ssh2_pkt_addstring(username);
ssh2_pkt_addstring("ssh-connection"); /* service requested */
ssh2_pkt_addstring("password");
ssh2_pkt_addbool(FALSE);
ssh2_pkt_addstring(password);
- ssh2_pkt_send();
+ ssh2_pkt_defer();
+ /*
+ * We'll include a string that's an exact multiple of the
+ * cipher block size. If the cipher is NULL for some
+ * reason, we don't do this trick at all because we gain
+ * nothing by it.
+ */
+ if (cscipher) {
+ int i, j;
+ ssh2_pkt_init(SSH2_MSG_IGNORE);
+ ssh2_pkt_addstring_start();
+ for (i = deferred_len; i <= 256; i += cscipher->blksize) {
+ for (j = 0; j < cscipher->blksize; j++) {
+ char c = (char)random_byte();
+ ssh2_pkt_addstring_data(&c, 1);
+ }
+ }
+ ssh2_pkt_defer();
+ }
+ ssh2_pkt_defersend();
crWaitUntilV(ispkt);
if (pktin.type != SSH2_MSG_USERAUTH_SUCCESS) {
crReturnV;
}
c_write("Server refused to allocate pty\r\n", 32);
+ ssh_editing = ssh_echoing = 1;
} else {
logevent("Allocated pty");
}
+ } else {
+ ssh_editing = ssh_echoing = 1;
}
/*
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
- if (*cfg.remote_cmd) {
+ if (cfg.ssh_subsys) {
+ ssh2_pkt_addstring("subsystem");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.remote_cmd);
+ } else if (*cfg.remote_cmd) {
ssh2_pkt_addstring("exec");
ssh2_pkt_addbool(1); /* want reply */
ssh2_pkt_addstring(cfg.remote_cmd);
/*
* Transfer data!
*/
+ ldisc_send(NULL, 0); /* cause ldisc to notice changes */
ssh_send_ok = 1;
- begin_session();
while (1) {
static int try_send;
crReturnV;
c = smalloc(sizeof(struct ssh_channel));
if (typelen == 3 && !memcmp(type, "x11", 3)) {
- char *rh;
if (!ssh_X11_fwd_enabled)
error = "X11 forwarding is not enabled";
- else if ( x11_init(&c->u.x11.s, cfg.x11_display, c, &rh) != NULL ) {
+ else if ( x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL ) {
error = "Unable to open an X11 connection";
} else {
c->type = CHAN_X11;
#endif
ssh_send_ok = 0;
+ ssh_editing = 0;
+ ssh_echoing = 0;
p = connect_to_host(host, port, realhost);
if (p != NULL)
static int ssh_sendok(void) { return ssh_send_ok; }
+static int ssh_ldisc(int option) {
+ if (option == LD_ECHO) return ssh_echoing;
+ if (option == LD_EDIT) return ssh_editing;
+ return FALSE;
+}
+
Backend ssh_backend = {
ssh_init,
ssh_send,
ssh_special,
ssh_socket,
ssh_sendok,
+ ssh_ldisc,
22
};