+ 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);
+ }
+
+ logevent("Doing Diffie-Hellman key exchange");
+ /*
+ * 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->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);
+
+ sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
+ if (ssh->kex == &ssh_diffiehellman_gex) {
+ sha_uint32(&ssh->exhash, s->pbits);
+ sha_mpint(&ssh->exhash, s->p);
+ sha_mpint(&ssh->exhash, s->g);
+ }
+ sha_mpint(&ssh->exhash, s->e);
+ sha_mpint(&ssh->exhash, s->f);
+ sha_mpint(&ssh->exhash, s->K);
+ SHA_Final(&ssh->exhash, s->exchange_hash);
+
+ dh_cleanup(ssh->kex_ctx);
+ ssh->kex_ctx = NULL;
+
+#if 0
+ debug(("Exchange hash is:\n"));
+ dmemdump(s->exchange_hash, 20);
+#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, 20)) {
+ bombout(("Server's host key did not match the signature supplied"));
+ crStop(0);
+ }
+
+ /*
+ * Authenticate remote host: verify host key. (We've already
+ * checked the signature of the exchange hash.)
+ */
+ s->keystr = ssh->hostkey->fmtkey(s->hkey);
+ s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
+ ssh_set_frozen(ssh, 1);
+ s->dlgret = verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport,
+ ssh->hostkey->keytype, s->keystr,
+ s->fingerprint,
+ ssh_dialog_callback, ssh);
+ if (s->dlgret < 0) {
+ do {
+ crReturn(0);
+ if (pktin) {
+ bombout(("Unexpected data from server while waiting"
+ " for user host key response"));
+ crStop(0);
+ }
+ } while (pktin || inlen > 0);
+ s->dlgret = ssh->user_response;
+ }
+ ssh_set_frozen(ssh, 0);
+ if (s->dlgret == 0) {
+ ssh->close_expected = TRUE;
+ ssh_closing((Plug)ssh, NULL, 0, 0);
+ crStop(0);
+ }
+ if (!s->got_session_id) { /* don't bother logging this in rekeys */
+ logevent("Host key fingerprint is:");
+ logevent(s->fingerprint);
+ }
+ sfree(s->fingerprint);
+ sfree(s->keystr);
+ ssh->hostkey->freekey(s->hkey);
+
+ /*
+ * The exchange hash from the very first key exchange is also
+ * the session id, used in session key construction and
+ * authentication.
+ */
+ if (!s->got_session_id) {
+ memcpy(ssh->v2_session_id, s->exchange_hash,
+ sizeof(s->exchange_hash));
+ s->got_session_id = TRUE;
+ }
+
+ /*
+ * Send SSH2_MSG_NEWKEYS.
+ */
+ s->pktout = ssh2_pkt_init(SSH2_MSG_NEWKEYS);
+ ssh2_pkt_send_noqueue(ssh, s->pktout);
+ ssh->outgoing_data_size = 0; /* start counting from here */
+
+ /*
+ * We've sent client NEWKEYS, so create and initialise
+ * client-to-server session keys.
+ */
+ if (ssh->cs_cipher_ctx)
+ ssh->cscipher->free_context(ssh->cs_cipher_ctx);
+ ssh->cscipher = s->cscipher_tobe;
+ ssh->cs_cipher_ctx = ssh->cscipher->make_context();
+
+ if (ssh->cs_mac_ctx)
+ ssh->csmac->free_context(ssh->cs_mac_ctx);
+ ssh->csmac = s->csmac_tobe;
+ ssh->cs_mac_ctx = ssh->csmac->make_context();
+
+ if (ssh->cs_comp_ctx)
+ ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
+ ssh->cscomp = s->cscomp_tobe;
+ ssh->cs_comp_ctx = ssh->cscomp->compress_init();
+
+ /*
+ * Set IVs on client-to-server keys. Here we use the exchange
+ * hash from the _first_ key exchange.
+ */
+ {
+ unsigned char keyspace[40];
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace);
+ ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace);
+ ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace);
+ ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
+ }
+
+ logeventf(ssh, "Initialised %.200s client->server encryption",
+ ssh->cscipher->text_name);
+ logeventf(ssh, "Initialised %.200s client->server MAC algorithm",
+ ssh->csmac->text_name);
+ if (ssh->cscomp->text_name)
+ logeventf(ssh, "Initialised %s compression",
+ ssh->cscomp->text_name);
+
+ /*
+ * Now our end of the key exchange is complete, we can send all
+ * our queued higher-layer packets.
+ */
+ ssh->queueing = FALSE;
+ ssh2_pkt_queuesend(ssh);
+
+ /*
+ * Expect SSH2_MSG_NEWKEYS from server.
+ */
+ crWaitUntil(pktin);
+ if (pktin->type != SSH2_MSG_NEWKEYS) {
+ bombout(("expected new-keys packet from server"));
+ crStop(0);
+ }
+ ssh->incoming_data_size = 0; /* start counting from here */
+
+ /*
+ * We've seen server NEWKEYS, so create and initialise
+ * server-to-client session keys.
+ */
+ if (ssh->sc_cipher_ctx)
+ ssh->sccipher->free_context(ssh->sc_cipher_ctx);
+ ssh->sccipher = s->sccipher_tobe;
+ ssh->sc_cipher_ctx = ssh->sccipher->make_context();
+
+ if (ssh->sc_mac_ctx)
+ ssh->scmac->free_context(ssh->sc_mac_ctx);
+ ssh->scmac = s->scmac_tobe;
+ ssh->sc_mac_ctx = ssh->scmac->make_context();
+
+ if (ssh->sc_comp_ctx)
+ ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);
+ ssh->sccomp = s->sccomp_tobe;
+ ssh->sc_comp_ctx = ssh->sccomp->decompress_init();
+
+ /*
+ * Set IVs on server-to-client keys. Here we use the exchange
+ * hash from the _first_ key exchange.
+ */
+ {
+ unsigned char keyspace[40];
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace);
+ ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace);
+ ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
+ ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace);
+ ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
+ }
+ logeventf(ssh, "Initialised %.200s server->client encryption",
+ ssh->sccipher->text_name);
+ logeventf(ssh, "Initialised %.200s server->client MAC algorithm",
+ ssh->scmac->text_name);
+ if (ssh->sccomp->text_name)
+ logeventf(ssh, "Initialised %s decompression",
+ ssh->sccomp->text_name);
+
+ /*
+ * Free key exchange data.
+ */
+ freebn(s->f);
+ freebn(s->K);
+ if (ssh->kex == &ssh_diffiehellman_gex) {
+ freebn(s->g);
+ freebn(s->p);
+ }
+
+ /*
+ * Key exchange is over. Loop straight back round if we have a
+ * deferred rekey reason.
+ */
+ if (ssh->deferred_rekey_reason) {
+ logevent(ssh->deferred_rekey_reason);
+ pktin = NULL;
+ ssh->deferred_rekey_reason = NULL;
+ goto begin_key_exchange;
+ }
+
+ /*
+ * Otherwise, schedule a timer for our next rekey.
+ */
+ ssh->kex_in_progress = FALSE;
+ ssh->last_rekey = GETTICKCOUNT();
+ if (ssh->cfg.ssh_rekey_time != 0)
+ ssh->next_rekey = schedule_timer(ssh->cfg.ssh_rekey_time*60*TICKSPERSEC,
+ ssh2_timer, ssh);
+
+ /*
+ * If this is the first key exchange phase, we must pass the
+ * SSH2_MSG_NEWKEYS packet to the next layer, not because it
+ * wants to see it but because it will need time to initialise
+ * itself before it sees an actual packet. In subsequent key
+ * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because
+ * it would only confuse the layer above.
+ */
+ if (s->activated_authconn) {
+ crReturn(1);
+ }
+ s->activated_authconn = TRUE;
+
+ /*
+ * Now we're encrypting. Begin returning 1 to the protocol main
+ * function so that other things can run on top of the
+ * transport. If we ever see a KEXINIT, we must go back to the
+ * start.
+ *
+ * We _also_ go back to the start if we see pktin==NULL and
+ * inlen==-1, because this is a special signal meaning
+ * `initiate client-driven rekey', and `in' contains a message
+ * giving the reason for the rekey.
+ */
+ while (!((pktin && pktin->type == SSH2_MSG_KEXINIT) ||
+ (!pktin && inlen == -1))) {
+ wait_for_rekey:
+ crReturn(1);
+ }
+ if (pktin) {
+ logevent("Server initiated key re-exchange");