- (dtucker) [Makefile.in added roaming_client.c roaming_serv.c] Import new
   files for roaming and add to Makefile.
diff --git a/ChangeLog b/ChangeLog
index 05b99b1..dbe3c0c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -154,6 +154,8 @@
      [sftp-server.c]
      bz#1566 don't unnecessarily dup() in and out fds for sftp-server;
      ok markus@
+ - (dtucker) [Makefile.in added roaming_client.c roaming_serv.c] Import new
+   files for roaming and add to Makefile.
 
 20091226
  - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1
diff --git a/Makefile.in b/Makefile.in
index da66311..a16782d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.301 2009/10/02 01:50:55 djm Exp $
+# $Id: Makefile.in,v 1.302 2010/01/08 08:13:25 dtucker Exp $
 
 # uncomment if you run a non bourne compatable shell. Ie. csh
 #SHELL = @SH@
@@ -75,7 +75,7 @@
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
-	roaming_common.o
+	roaming_common.o roaming_client.c
 
 SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
 	sshpty.o sshlogin.o servconf.o serverloop.o \
@@ -88,7 +88,7 @@
 	auth2-gss.o gss-serv.o gss-serv-krb5.o \
 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
 	audit.o audit-bsm.o platform.o sftp-server.o sftp-common.o \
-	roaming_common.o
+	roaming_common.o roaming_serv.c
 
 MANPAGES	= moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out
 MANPAGES_IN	= moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5
diff --git a/roaming_client.c b/roaming_client.c
new file mode 100644
index 0000000..b77dbd5
--- /dev/null
+++ b/roaming_client.c
@@ -0,0 +1,276 @@
+/* $OpenBSD: roaming_client.c,v 1.1 2009/10/24 11:22:37 andreas Exp $ */
+/*
+ * Copyright (c) 2004-2009 AppGate Network Security AB
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <inttypes.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/crypto.h>
+#include <openssl/sha.h>
+
+#include "xmalloc.h"
+#include "buffer.h"
+#include "channels.h"
+#include "cipher.h"
+#include "dispatch.h"
+#include "clientloop.h"
+#include "log.h"
+#include "match.h"
+#include "misc.h"
+#include "packet.h"
+#include "ssh.h"
+#include "key.h"
+#include "kex.h"
+#include "readconf.h"
+#include "roaming.h"
+#include "ssh2.h"
+#include "sshconnect.h"
+
+/* import */
+extern Options options;
+extern char *host;
+extern struct sockaddr_storage hostaddr;
+extern int session_resumed;
+
+static u_int32_t roaming_id;
+static u_int64_t cookie;
+static u_int64_t lastseenchall;
+static u_int64_t key1, key2, oldkey1, oldkey2;
+
+void
+roaming_reply(int type, u_int32_t seq, void *ctxt)
+{
+	if (type == SSH2_MSG_REQUEST_FAILURE) {
+		logit("Server denied roaming");
+		return;
+	}
+	verbose("Roaming enabled");
+	roaming_id = packet_get_int();
+	cookie = packet_get_int64();
+	key1 = oldkey1 = packet_get_int64();
+	key2 = oldkey2 = packet_get_int64();
+	set_out_buffer_size(packet_get_int() +  get_snd_buf_size());
+	roaming_enabled = 1;
+}
+
+void
+request_roaming(void)
+{
+	packet_start(SSH2_MSG_GLOBAL_REQUEST);
+	packet_put_cstring(ROAMING_REQUEST);
+	packet_put_char(1);
+	packet_put_int(get_recv_buf_size());
+	packet_send();
+	client_register_global_confirm(roaming_reply, NULL);
+}
+
+static void
+roaming_auth_required(void)
+{
+	u_char digest[SHA_DIGEST_LENGTH];
+	EVP_MD_CTX md;
+	Buffer b;
+	const EVP_MD *evp_md = EVP_sha1();
+	u_int64_t chall, oldchall;
+
+	chall = packet_get_int64();
+	oldchall = packet_get_int64();
+	if (oldchall != lastseenchall) {
+		key1 = oldkey1;
+		key2 = oldkey2;
+	}
+	lastseenchall = chall;
+
+	buffer_init(&b);
+	buffer_put_int64(&b, cookie);
+	buffer_put_int64(&b, chall);
+	EVP_DigestInit(&md, evp_md);
+	EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+	EVP_DigestFinal(&md, digest, NULL);
+	buffer_free(&b);
+
+	packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
+	packet_put_int64(key1 ^ get_recv_bytes());
+	packet_put_raw(digest, sizeof(digest));
+	packet_send();
+
+	oldkey1 = key1;
+	oldkey2 = key2;
+	calculate_new_key(&key1, cookie, chall);
+	calculate_new_key(&key2, cookie, chall);
+
+	debug("Received %" PRIu64 " bytes", get_recv_bytes());
+	debug("Sent roaming_auth packet");
+}
+
+int
+resume_kex(void)
+{
+	/*
+	 * This should not happen - if the client sends the kex method
+	 * resume@appgate.com then the kex is done in roaming_resume().
+	 */
+	return 1;
+}
+
+static int
+roaming_resume(void)
+{
+	u_int64_t recv_bytes;
+	char *str = NULL, *kexlist = NULL, *c;
+	int i, type;
+	int timeout_ms = options.connection_timeout * 1000;
+	u_int len;
+	u_int32_t rnd = 0;
+
+	resume_in_progress = 1;
+
+	/* Exchange banners */
+	ssh_exchange_identification(timeout_ms);
+	packet_set_nonblocking();
+
+	/* Send a kexinit message with resume@appgate.com as only kex algo */
+	packet_start(SSH2_MSG_KEXINIT);
+	for (i = 0; i < KEX_COOKIE_LEN; i++) {
+		if (i % 4 == 0)
+			rnd = arc4random();
+		packet_put_char(rnd & 0xff);
+		rnd >>= 8;
+	}
+	packet_put_cstring(KEX_RESUME);
+	for (i = 1; i < PROPOSAL_MAX; i++) {
+		/* kex algorithm added so start with i=1 and not 0 */
+		packet_put_cstring(""); /* Not used when we resume */
+	}
+	packet_put_char(1); /* first kex_packet follows */
+	packet_put_int(0); /* reserved */
+	packet_send();
+
+	/* Assume that resume@appgate.com will be accepted */
+	packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
+	packet_put_int(roaming_id);
+	packet_send();
+
+	/* Read the server's kexinit and check for resume@appgate.com */
+	if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
+		debug("expected kexinit on resume, got %d", type);
+		goto fail;
+	}
+	for (i = 0; i < KEX_COOKIE_LEN; i++)
+		(void)packet_get_char();
+	kexlist = packet_get_string(&len);
+	if (!kexlist
+	    || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
+		debug("server doesn't allow resume");
+		goto fail;
+	}
+	xfree(str);
+	for (i = 1; i < PROPOSAL_MAX; i++) {
+		/* kex algorithm taken care of so start with i=1 and not 0 */
+		xfree(packet_get_string(&len));
+	}
+	i = packet_get_char(); /* first_kex_packet_follows */
+	if (i && (c = strchr(kexlist, ',')))
+		*c = 0;
+	if (i && strcmp(kexlist, KEX_RESUME)) {
+		debug("server's kex guess (%s) was wrong, skipping", kexlist);
+		(void)packet_read(); /* Wrong guess - discard packet */
+	}
+
+	/*
+	 * Read the ROAMING_AUTH_REQUIRED challenge from the server and
+	 * send ROAMING_AUTH
+	 */
+	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
+		debug("expected roaming_auth_required, got %d", type);
+		goto fail;
+	}
+	roaming_auth_required();
+
+	/* Read ROAMING_AUTH_OK from the server */
+	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
+		debug("expected roaming_auth_ok, got %d", type);
+		goto fail;
+	}
+	recv_bytes = packet_get_int64() ^ oldkey2;
+	debug("Peer received %" PRIu64 " bytes", recv_bytes);
+	resend_bytes(packet_get_connection_out(), &recv_bytes);
+
+	resume_in_progress = 0;
+
+	session_resumed = 1; /* Tell clientloop */
+
+	return 0;
+
+fail:
+	if (kexlist)
+		xfree(kexlist);
+	if (packet_get_connection_in() == packet_get_connection_out())
+		close(packet_get_connection_in());
+	else {
+		close(packet_get_connection_in());
+		close(packet_get_connection_out());
+	}
+	return 1;
+}
+
+int
+wait_for_roaming_reconnect(void)
+{
+	static int reenter_guard = 0;
+	int timeout_ms = options.connection_timeout * 1000;
+	int c;
+
+	if (reenter_guard != 0)
+		fatal("Server refused resume, roaming timeout may be exceeded");
+	reenter_guard = 1;
+
+	fprintf(stderr, "[connection suspended, press return to resume]");
+	fflush(stderr);
+	packet_backup_state();
+	/* TODO Perhaps we should read from tty here */
+	while ((c = fgetc(stdin)) != EOF) {
+		if (c == 'Z' - 64) {
+			kill(getpid(), SIGTSTP);
+			continue;
+		}
+		if (c != '\n' && c != '\r')
+			continue;
+
+		if (ssh_connect(host, &hostaddr, options.port,
+		    options.address_family, 1, &timeout_ms,
+		    options.tcp_keep_alive, options.use_privileged_port,
+		    options.proxy_command) == 0 && roaming_resume() == 0) {
+			packet_restore_state();
+			reenter_guard = 0;
+			fprintf(stderr, "[connection resumed]\n");
+			fflush(stderr);
+			return 0;
+		}
+
+		fprintf(stderr, "[reconnect failed, press return to retry]");
+		fflush(stderr);
+	}
+	fprintf(stderr, "[exiting]\n");
+	fflush(stderr);
+	exit(0);
+}
diff --git a/roaming_serv.c b/roaming_serv.c
new file mode 100644
index 0000000..65e9fe6
--- /dev/null
+++ b/roaming_serv.c
@@ -0,0 +1,29 @@
+/* $OpenBSD: roaming_serv.c,v 1.1 2009/10/24 11:18:23 andreas Exp $ */
+/*
+ * Copyright (c) 2004-2009 AppGate Network Security AB
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include "roaming.h"
+
+/*
+ * Wait for the roaming client to reconnect. Returns 0 if a connect ocurred.
+ */
+int
+wait_for_roaming_reconnect(void)
+{
+	return 1;
+}