+ 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);
+
+ crWaitUntil(pktin);
+ if (pktin->type != s->kex_reply_value) {
+ bombout(("expected key exchange reply packet from server"));
+ crStop(0);
+ }
+ 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);
+
+ 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);
+ verify_ssh_host_key(ssh->frontend,
+ ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
+ s->keystr, s->fingerprint);
+ 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))) {
+ crReturn(1);
+ }
+ if (pktin) {
+ logevent("Server initiated key re-exchange");
+ } else {
+ logevent((char *)in);
+ }
+ goto begin_key_exchange;
+
+ crFinish(1);
+}
+
+/*
+ * Add data to an SSH2 channel output buffer.
+ */
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
+ int len)
+{
+ bufchain_add(&c->v.v2.outbuffer, buf, len);
+}
+
+/*
+ * Attempt to send data on an SSH2 channel.
+ */
+static int ssh2_try_send(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
+ int len;
+ void *data;
+ bufchain_prefix(&c->v.v2.outbuffer, &data, &len);
+ if ((unsigned)len > c->v.v2.remwindow)
+ len = c->v.v2.remwindow;
+ if ((unsigned)len > c->v.v2.remmaxpkt)
+ len = c->v.v2.remmaxpkt;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ dont_log_data(ssh, pktout, PKTLOG_OMIT);
+ ssh2_pkt_addstring_start(pktout);
+ ssh2_pkt_addstring_data(pktout, data, len);
+ end_log_omission(ssh, pktout);
+ ssh2_pkt_send(ssh, pktout);
+ bufchain_consume(&c->v.v2.outbuffer, len);
+ c->v.v2.remwindow -= len;
+ }
+
+ /*
+ * After having sent as much data as we can, return the amount
+ * still buffered.
+ */
+ return bufchain_size(&c->v.v2.outbuffer);
+}
+
+/*
+ * Potentially enlarge the window on an SSH2 channel.
+ */
+static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
+{
+ Ssh ssh = c->ssh;
+
+ /*
+ * Never send WINDOW_ADJUST for a channel that the remote side
+ * already thinks it's closed; there's no point, since it won't
+ * be sending any more data anyway.
+ */
+ if (c->closes != 0)
+ return;
+
+ if (newwin > c->v.v2.locwindow) {
+ struct Packet *pktout;
+
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);
+ ssh2_pkt_send(ssh, pktout);
+ c->v.v2.locwindow = newwin;
+ }
+}
+
+static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
+{
+ unsigned i = ssh_pkt_getuint32(pktin);
+ struct ssh_channel *c;
+ c = find234(ssh->channels, &i, ssh_channelfind);
+ if (c && !c->closes)
+ c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
+}