summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorAndreas Gunnarsson <andreas@cvs.openbsd.org>2009-10-24 11:22:38 +0000
committerAndreas Gunnarsson <andreas@cvs.openbsd.org>2009-10-24 11:22:38 +0000
commit17c3bbf5c122e8768982eab57386ec0f705b4a00 (patch)
tree2211a4282f2955cd03d5f3aadcd2255e377d7876 /usr.bin
parent5e3c242f20c6ce3b12ceac90c9fafc1ac08ecd70 (diff)
Do the actual suspend/resume in the client. This won't be useful until
the server side supports roaming. Most code from Martin Forssen, maf at appgate dot com. Some changes by me and markus@ ok markus@
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/ssh/roaming_client.c276
-rw-r--r--usr.bin/ssh/roaming_common.c47
-rw-r--r--usr.bin/ssh/ssh/Makefile4
3 files changed, 324 insertions, 3 deletions
diff --git a/usr.bin/ssh/roaming_client.c b/usr.bin/ssh/roaming_client.c
new file mode 100644
index 00000000000..b77dbd59b9a
--- /dev/null
+++ b/usr.bin/ssh/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/usr.bin/ssh/roaming_common.c b/usr.bin/ssh/roaming_common.c
index f9806404287..3304ae98897 100644
--- a/usr.bin/ssh/roaming_common.c
+++ b/usr.bin/ssh/roaming_common.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: roaming_common.c,v 1.5 2009/06/27 09:32:43 andreas Exp $ */
+/* $OpenBSD: roaming_common.c,v 1.6 2009/10/24 11:22:37 andreas Exp $ */
/*
* Copyright (c) 2004-2009 AppGate Network Security AB
*
@@ -143,6 +143,16 @@ roaming_write(int fd, const void *buf, size_t count, int *cont)
}
debug3("Wrote %ld bytes for a total of %llu", (long)ret,
(unsigned long long)write_bytes);
+ if (out_buf_size > 0 &&
+ (ret == 0 || (ret == -1 && errno == EPIPE))) {
+ if (wait_for_roaming_reconnect() != 0) {
+ ret = 0;
+ *cont = 1;
+ } else {
+ ret = -1;
+ errno = EAGAIN;
+ }
+ }
return ret;
}
@@ -154,6 +164,15 @@ roaming_read(int fd, void *buf, size_t count, int *cont)
if (!resume_in_progress) {
read_bytes += ret;
}
+ } else if (out_buf_size > 0 &&
+ (ret == 0 || (ret == -1 && (errno == ECONNRESET
+ || errno == ECONNABORTED || errno == ETIMEDOUT
+ || errno == EHOSTUNREACH)))) {
+ debug("roaming_read failed for %d ret=%ld errno=%d",
+ fd, (long)ret, errno);
+ ret = 0;
+ if (wait_for_roaming_reconnect() == 0)
+ *cont = 1;
}
return ret;
}
@@ -195,3 +214,29 @@ resend_bytes(int fd, u_int64_t *offset)
atomicio(vwrite, fd, out_buf + (out_last - needed), needed);
}
}
+
+/*
+ * Caclulate a new key after a reconnect
+ */
+void
+calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge)
+{
+ const EVP_MD *md = EVP_sha1();
+ EVP_MD_CTX ctx;
+ char hash[EVP_MAX_MD_SIZE];
+ Buffer b;
+
+ buffer_init(&b);
+ buffer_put_int64(&b, *key);
+ buffer_put_int64(&b, cookie);
+ buffer_put_int64(&b, challenge);
+
+ EVP_DigestInit(&ctx, md);
+ EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b));
+ EVP_DigestFinal(&ctx, hash, NULL);
+
+ buffer_clear(&b);
+ buffer_append(&b, hash, EVP_MD_size(md));
+ *key = buffer_get_int64(&b);
+ buffer_free(&b);
+}
diff --git a/usr.bin/ssh/ssh/Makefile b/usr.bin/ssh/ssh/Makefile
index db2c08cc1ed..a4355be22b6 100644
--- a/usr.bin/ssh/ssh/Makefile
+++ b/usr.bin/ssh/ssh/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.53 2009/05/28 16:50:16 andreas Exp $
+# $OpenBSD: Makefile,v 1.54 2009/10/24 11:22:37 andreas Exp $
.PATH: ${.CURDIR}/..
@@ -14,7 +14,7 @@ MLINKS= ssh.1 slogin.1
SRCS= ssh.c readconf.c clientloop.c sshtty.c \
sshconnect.c sshconnect1.c sshconnect2.c mux.c \
- roaming_common.c
+ roaming_common.c roaming_client.c
.include <bsd.own.mk> # for AFS