+ 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;
+ }
+ if (cfg.compression)
+ preferred_comp = &ssh_zlib;
+ else
+ preferred_comp = &ssh_comp_none;
+
+ /*
+ * Be prepared to work around the buggy MAC problem.
+ */
+ if (cfg.buggymac)
+ maclist = buggymacs, nmacs = lenof(buggymacs);
+ else
+ maclist = macs, nmacs = lenof(macs);
+
+ begin_key_exchange:
+ /*
+ * Construct and send our key exchange packet.
+ */
+ ssh2_pkt_init(SSH2_MSG_KEXINIT);
+ for (i = 0; i < 16; i++)
+ ssh2_pkt_addbyte((unsigned char)random_byte());
+ /* List key exchange algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(kex_algs); i++) {
+ ssh2_pkt_addstring_str(kex_algs[i]->name);
+ if (i < lenof(kex_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server host key algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ ssh2_pkt_addstring_str(hostkey_algs[i]->name);
+ if (i < lenof(hostkey_algs)-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* 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 (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 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();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client MAC algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < nmacs; i++) {
+ ssh2_pkt_addstring_str(maclist[i]->name);
+ if (i < nmacs-1)
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(compressions))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List server->client compression algorithms. */
+ ssh2_pkt_addstring_start();
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ ssh2_pkt_addstring_str(c->name);
+ if (i < lenof(compressions))
+ ssh2_pkt_addstring_str(",");
+ }
+ /* List client->server languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* List server->client languages. Empty list. */
+ ssh2_pkt_addstring_start();
+ /* First KEX packet does _not_ follow, because we're not that brave. */
+ ssh2_pkt_addbool(FALSE);
+ /* Reserved. */
+ ssh2_pkt_adduint32(0);
+
+ exhash = exhashbase;
+ sha_string(&exhash, pktout.data+5, pktout.length-5);
+
+ ssh2_pkt_send();
+
+ if (!ispkt) crWaitUntil(ispkt);
+ sha_string(&exhash, pktin.data+5, pktin.length-5);
+
+ /*
+ * Now examine the other side's KEXINIT to see what we're up
+ * to.
+ */
+ if (pktin.type != SSH2_MSG_KEXINIT) {
+ bombout(("expected key exchange packet from server"));
+ crReturn(0);
+ }
+ kex = NULL; hostkey = NULL; cscipher_tobe = NULL; sccipher_tobe = NULL;
+ csmac_tobe = NULL; scmac_tobe = NULL; cscomp_tobe = NULL; sccomp_tobe = NULL;
+ pktin.savedpos += 16; /* skip garbage cookie */
+ ssh2_pkt_getstring(&str, &len); /* key exchange algorithms */
+ for (i = 0; i < lenof(kex_algs); i++) {
+ if (in_commasep_string(kex_algs[i]->name, str, len)) {
+ kex = kex_algs[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* host key algorithms */
+ for (i = 0; i < lenof(hostkey_algs); i++) {
+ if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
+ hostkey = hostkey_algs[i];
+ break;
+ }
+ }
+ 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;
+ }
+ }
+ if (cscipher_tobe)
+ 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;
+ }
+ }
+ if (sccipher_tobe)
+ break;
+ }
+ ssh2_pkt_getstring(&str, &len); /* client->server mac */
+ for (i = 0; i < nmacs; i++) {
+ if (in_commasep_string(maclist[i]->name, str, len)) {
+ csmac_tobe = maclist[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* server->client mac */
+ for (i = 0; i < nmacs; i++) {
+ if (in_commasep_string(maclist[i]->name, str, len)) {
+ scmac_tobe = maclist[i];
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* client->server compression */
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ if (in_commasep_string(c->name, str, len)) {
+ cscomp_tobe = c;
+ break;
+ }
+ }
+ ssh2_pkt_getstring(&str, &len); /* server->client compression */
+ for (i = 0; i < lenof(compressions)+1; i++) {
+ const struct ssh_compress *c = i==0 ? preferred_comp : compressions[i-1];
+ if (in_commasep_string(c->name, str, len)) {
+ sccomp_tobe = c;
+ break;
+ }
+ }
+
+ /*
+ * If we're doing Diffie-Hellman group exchange, start by
+ * requesting a group.
+ */
+ 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 generate and send e for Diffie-Hellman.
+ */
+ e = dh_create_e();
+ ssh2_pkt_init(kex_init_value);
+ ssh2_pkt_addmp(e);
+ ssh2_pkt_send();
+
+ crWaitUntil(ispkt);
+ if (pktin.type != kex_reply_value) {
+ bombout(("expected key exchange reply packet from server"));
+ crReturn(0);
+ }
+ ssh2_pkt_getstring(&hostkeydata, &hostkeylen);
+ f = ssh2_pkt_getmp();
+ ssh2_pkt_getstring(&sigdata, &siglen);
+
+ 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++)
+ debug((" %02x", exchange_hash[i]));
+ debug(("\r\n"));