#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 *);
extern void x11_close (Socket);
* 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;
/*
- * 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.
*/
* 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;
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;
*/
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 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.
*/
- dh_setup_group1();
-
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);
fingerprint = hostkey->fingerprint(hkey);
verify_ssh_host_key(savedhost, savedport, hostkey->keytype,
keystr, fingerprint);
- if (first_kex) { /* don't bother logging this in rekeys */
+ if (first_kex) { /* don't bother logging this in rekeys */
logevent("Host key fingerprint is:");
logevent(fingerprint);
}