Initial commit of openssh-5.9p1

Change-Id: I991b8d22c9904ed6ced33a096776f4f342af52a8
Signed-off-by: Mike Lockwood <lockwood@google.com>
diff --git a/roaming_client.c b/roaming_client.c
new file mode 100644
index 0000000..cea8e73
--- /dev/null
+++ b/roaming_client.c
@@ -0,0 +1,280 @@
+/* $OpenBSD: roaming_client.c,v 1.3 2010/01/18 01:50:27 dtucker 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 "includes.h"
+
+#include "openbsd-compat/sys-queue.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#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 %llu bytes", (unsigned long long)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 %llu bytes", (unsigned long long)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);
+}