- markus@cvs.openbsd.org 2001/04/04 20:25:38
     [channels.c channels.h clientloop.c kex.c kex.h serverloop.c
      sshconnect2.c sshd.c]
     more robust rekeying
     don't send channel data after rekeying is started.
diff --git a/ChangeLog b/ChangeLog
index 7bb8eaa..83c02ec 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,11 @@
    - markus@cvs.openbsd.org 2001/04/04 15:50:55
      [compat.c]
      f-secure 1.3.2 does not handle IGNORE; from milliondl@ornl.gov
+   - markus@cvs.openbsd.org 2001/04/04 20:25:38
+     [channels.c channels.h clientloop.c kex.c kex.h serverloop.c 
+      sshconnect2.c sshd.c]
+     more robust rekeying
+     don't send channel data after rekeying is started.
 
 20010404
  - OpenBSD CVS Sync
@@ -4851,4 +4856,4 @@
  - Wrote replacements for strlcpy and mkdtemp
  - Released 1.0pre1
 
-$Id: ChangeLog,v 1.1057 2001/04/04 23:43:26 mouring Exp $
+$Id: ChangeLog,v 1.1058 2001/04/04 23:46:07 mouring Exp $
diff --git a/channels.c b/channels.c
index 941556a..7790564 100644
--- a/channels.c
+++ b/channels.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.99 2001/03/16 19:06:29 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.100 2001/04/04 20:25:35 markus Exp $");
 
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
@@ -1005,7 +1005,8 @@
 }
 
 void
-channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp)
+channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
+    int rekeying)
 {
 	int n;
 	u_int sz;
@@ -1025,7 +1026,8 @@
 	memset(*readsetp, 0, sz);
 	memset(*writesetp, 0, sz);
 
-	channel_handler(channel_pre, *readsetp, *writesetp);
+	if (!rekeying)
+		channel_handler(channel_pre, *readsetp, *writesetp);
 }
 
 void
diff --git a/channels.h b/channels.h
index 493b04a..2cd8214 100644
--- a/channels.h
+++ b/channels.h
@@ -32,7 +32,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-/* RCSID("$OpenBSD: channels.h,v 1.28 2001/03/16 19:06:29 markus Exp $"); */
+/* RCSID("$OpenBSD: channels.h,v 1.29 2001/04/04 20:25:36 markus Exp $"); */
 
 #ifndef CHANNELS_H
 #define CHANNELS_H
@@ -171,7 +171,8 @@
  * select bitmasks.
  */
 void
-channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp);
+channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
+    int rekeying);
 
 /*
  * After select, perform any appropriate operations for channels which have
diff --git a/clientloop.c b/clientloop.c
index 1d09a8d..4b87e3b 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -59,7 +59,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.55 2001/04/04 14:34:58 markus Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.56 2001/04/04 20:25:37 markus Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -127,6 +127,7 @@
 static u_int buffer_high;/* Soft max buffer size. */
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
+static int need_rekeying;	/* Set to non-zero if rekeying is requested. */
 
 void	client_init_dispatch(void);
 int	session_ident = -1;
@@ -367,10 +368,10 @@
 
 void
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
-    int *maxfdp)
+    int *maxfdp, int rekeying)
 {
 	/* Add any selections by the channel mechanism. */
-	channel_prepare_select(readsetp, writesetp, maxfdp);
+	channel_prepare_select(readsetp, writesetp, maxfdp, rekeying);
 
 	if (!compat20) {
 		/* Read from the connection, unless our buffers are full. */
@@ -553,8 +554,8 @@
 				continue;
 
 			case 'R':
-				debug("Rekeying");
-				kex_send_kexinit(xxx_kex);
+				if (compat20)
+					need_rekeying = 1;
 				continue;
 
 			case '&':
@@ -794,9 +795,8 @@
 client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 {
 	fd_set *readset = NULL, *writeset = NULL;
-	int max_fd = 0;
 	double start_time, total_time;
-	int len;
+	int max_fd = 0, len, rekeying = 0;
 	char buf[100];
 
 	debug("Entering interactive session.");
@@ -858,45 +858,60 @@
 		/* Process buffered packets sent by the server. */
 		client_process_buffered_input_packets();
 
+		rekeying = (xxx_kex != NULL && !xxx_kex->done);
+
 		if (compat20 && !channel_still_open()) {
 			debug2("!channel_still_open.");
 			break;
 		}
 
-		/*
-		 * Make packets of buffered stdin data, and buffer them for
-		 * sending to the server.
-		 */
-		if (!compat20)
-			client_make_packets_from_stdin_data();
+		if (rekeying) {
+			debug("rekeying in progress");
+		} else {
+			/*
+			 * Make packets of buffered stdin data, and buffer
+			 * them for sending to the server.
+			 */
+			if (!compat20)
+				client_make_packets_from_stdin_data();
 
-		/*
-		 * Make packets from buffered channel data, and enqueue them
-		 * for sending to the server.
-		 */
-		if (packet_not_very_much_data_to_write())
-			channel_output_poll();
+			/*
+			 * Make packets from buffered channel data, and
+			 * enqueue them for sending to the server.
+			 */
+			if (packet_not_very_much_data_to_write())
+				channel_output_poll();
 
-		/*
-		 * Check if the window size has changed, and buffer a message
-		 * about it to the server if so.
-		 */
-		client_check_window_change();
+			/*
+			 * Check if the window size has changed, and buffer a
+			 * message about it to the server if so.
+			 */
+			client_check_window_change();
 
-		if (quit_pending)
-			break;
-
+			if (quit_pending)
+				break;
+		}
 		/*
 		 * Wait until we have something to do (something becomes
 		 * available on one of the descriptors).
 		 */
-		client_wait_until_can_do_something(&readset, &writeset, &max_fd);
+		client_wait_until_can_do_something(&readset, &writeset,
+		    &max_fd, rekeying);
 
 		if (quit_pending)
 			break;
 
-		/* Do channel operations. */
-		channel_after_select(readset, writeset);
+		/* Do channel operations unless rekeying in progress. */
+		if (!rekeying) {
+			channel_after_select(readset, writeset);
+
+			if (need_rekeying) {
+				debug("user requests rekeying");
+				xxx_kex->done = 0;
+				kex_send_kexinit(xxx_kex);
+				need_rekeying = 0;
+			}
+		}
 
 		/* Buffer input from the connection.  */
 		client_process_net_input(readset);
diff --git a/kex.c b/kex.c
index ee1e17e..da9c56e 100644
--- a/kex.c
+++ b/kex.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: kex.c,v 1.29 2001/04/04 14:34:58 markus Exp $");
+RCSID("$OpenBSD: kex.c,v 1.30 2001/04/04 20:25:37 markus Exp $");
 
 #include <openssl/crypto.h>
 
@@ -136,7 +136,7 @@
         debug("waiting for SSH2_MSG_NEWKEYS");
         packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
 	debug("SSH2_MSG_NEWKEYS received");
-	kex->newkeys = 1;
+	kex->done = 1;
 	buffer_clear(&kex->peer);
 	/* buffer_clear(&kex->my); */
 	kex->flags &= ~KEX_INIT_SENT;
@@ -153,6 +153,7 @@
 		debug("KEX_INIT_SENT");
 		return;
 	}
+	kex->done = 0;
 	packet_start(SSH2_MSG_KEXINIT);
 	packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
 	packet_send();
@@ -187,7 +188,7 @@
 	buffer_init(&kex->peer);
 	buffer_init(&kex->my);
 	kex_prop2buf(&kex->my, proposal);
-	kex->newkeys = 0;
+	kex->done = 0;
 
 	kex_send_kexinit(kex);					/* we start */
 	kex_clear_dispatch();
@@ -307,10 +308,11 @@
 		sprop=peer;
 	}
 
+	/* Algorithm Negotiation */
 	for (mode = 0; mode < MODE_MAX; mode++) {
 		newkeys = xmalloc(sizeof(*newkeys));
 		memset(newkeys, 0, sizeof(*newkeys));
-		kex->keys[mode] = newkeys;
+		kex->newkeys[mode] = newkeys;
 		ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
 		nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
 		nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
@@ -329,7 +331,7 @@
 	    sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
 	need = 0;
 	for (mode = 0; mode < MODE_MAX; mode++) {
-		newkeys = kex->keys[mode];
+		newkeys = kex->newkeys[mode];
 		if (need < newkeys->enc.cipher->key_len)
 			need = newkeys->enc.cipher->key_len;
 		if (need < newkeys->enc.cipher->block_size)
@@ -353,19 +355,24 @@
 	char c = id;
 	int have;
 	int mdsz = evp_md->md_size;
-	u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
+	u_char *digest = xmalloc(roundup(need, mdsz));
 
 	buffer_init(&b);
 	buffer_put_bignum2(&b, shared_secret);
 
+	/* K1 = HASH(K || H || "A" || session_id) */
 	EVP_DigestInit(&md, evp_md);
-	EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));	/* shared_secret K */
-	EVP_DigestUpdate(&md, hash, mdsz);		/* transport-06 */
-	EVP_DigestUpdate(&md, &c, 1);			/* key id */
+	EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+	EVP_DigestUpdate(&md, hash, mdsz);
+	EVP_DigestUpdate(&md, &c, 1);
 	EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len);
 	EVP_DigestFinal(&md, digest, NULL);
 
-	/* expand */
+	/*
+	 * expand key:
+	 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
+	 * Key = K1 || K2 || ... || Kn
+	 */
 	for (have = mdsz; need > have; have += mdsz) {
 		EVP_DigestInit(&md, evp_md);
 		EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
@@ -381,13 +388,12 @@
 	return digest;
 }
 
-Newkeys *x_newkeys[MODE_MAX];
+Newkeys *current_keys[MODE_MAX];
 
 #define NKEYS	6
 void
 kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret)
 {
-	Newkeys *newkeys;
 	u_char *keys[NKEYS];
 	int i, mode, ctos;
 
@@ -396,19 +402,23 @@
 
 	debug("kex_derive_keys");
 	for (mode = 0; mode < MODE_MAX; mode++) {
-		newkeys = kex->keys[mode];
+		current_keys[mode] = kex->newkeys[mode];
+		kex->newkeys[mode] = NULL;
 		ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
-		newkeys->enc.iv  = keys[ctos ? 0 : 1];
-		newkeys->enc.key = keys[ctos ? 2 : 3];
-		newkeys->mac.key = keys[ctos ? 4 : 5];
-		x_newkeys[mode] = newkeys;
+		current_keys[mode]->enc.iv  = keys[ctos ? 0 : 1];
+		current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
+		current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
 	}
 }
 
 Newkeys *
 kex_get_newkeys(int mode)
 {
-	return x_newkeys[mode];
+	Newkeys *ret;
+
+	ret = current_keys[mode];
+	current_keys[mode] = NULL;
+	return ret;
 }
 
 #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
diff --git a/kex.h b/kex.h
index 5413422..8758804 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: kex.h,v 1.21 2001/04/04 14:34:58 markus Exp $	*/
+/*	$OpenBSD: kex.h,v 1.22 2001/04/04 20:25:37 markus Exp $	*/
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -95,7 +95,7 @@
 struct Kex {
 	u_char	*session_id;
 	int	session_id_len;
-	Newkeys	*keys[MODE_MAX];
+	Newkeys	*newkeys[MODE_MAX];
 	int	we_need;
 	int	server;
 	char	*name;
@@ -103,7 +103,7 @@
 	int	kex_type;
 	Buffer	my;
 	Buffer	peer;
-	int	newkeys;
+	int	done;
 	int	flags;
 	char	*client_version_string;
 	char	*server_version_string;
diff --git a/serverloop.c b/serverloop.c
index 4ae02fd..ab7472b 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -35,7 +35,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.56 2001/04/04 14:34:58 markus Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.57 2001/04/04 20:25:37 markus Exp $");
 
 #include "xmalloc.h"
 #include "packet.h"
@@ -194,7 +194,7 @@
 retry_select:
 
 	/* Allocate and update select() masks for channel descriptors. */
-	channel_prepare_select(readsetp, writesetp, maxfdp);
+	channel_prepare_select(readsetp, writesetp, maxfdp, 0);
 
 	if (compat20) {
 		/* wrong: bad condition XXX */
diff --git a/sshconnect2.c b/sshconnect2.c
index 2f26aa5..918ab38 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.65 2001/04/04 14:34:58 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.66 2001/04/04 20:25:38 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/md5.h>
@@ -119,7 +119,7 @@
 
 	xxx_kex = kex;
 
-	dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex);
+	dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
 
 	session_id2 = kex->session_id;
 	session_id2_len = kex->session_id_len;
diff --git a/sshd.c b/sshd.c
index ea29e75..5b59288 100644
--- a/sshd.c
+++ b/sshd.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.189 2001/04/04 14:34:58 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.190 2001/04/04 20:25:38 markus Exp $");
 
 #include <openssl/dh.h>
 #include <openssl/bn.h>
@@ -1437,7 +1437,7 @@
 
 	xxx_kex = kex;
 
-	dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex);
+	dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
 
 	session_id2 = kex->session_id;
 	session_id2_len = kex->session_id_len;