Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: roaming_client.c,v 1.3 2010/01/18 01:50:27 dtucker Exp $ */
      2 /*
      3  * Copyright (c) 2004-2009 AppGate Network Security AB
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 
     18 #include "includes.h"
     19 
     20 #include "openbsd-compat/sys-queue.h"
     21 #include <sys/types.h>
     22 #include <sys/socket.h>
     23 
     24 #ifdef HAVE_INTTYPES_H
     25 #include <inttypes.h>
     26 #endif
     27 #include <signal.h>
     28 #include <string.h>
     29 #include <unistd.h>
     30 
     31 #include <openssl/crypto.h>
     32 #include <openssl/sha.h>
     33 
     34 #include "xmalloc.h"
     35 #include "buffer.h"
     36 #include "channels.h"
     37 #include "cipher.h"
     38 #include "dispatch.h"
     39 #include "clientloop.h"
     40 #include "log.h"
     41 #include "match.h"
     42 #include "misc.h"
     43 #include "packet.h"
     44 #include "ssh.h"
     45 #include "key.h"
     46 #include "kex.h"
     47 #include "readconf.h"
     48 #include "roaming.h"
     49 #include "ssh2.h"
     50 #include "sshconnect.h"
     51 
     52 /* import */
     53 extern Options options;
     54 extern char *host;
     55 extern struct sockaddr_storage hostaddr;
     56 extern int session_resumed;
     57 
     58 static u_int32_t roaming_id;
     59 static u_int64_t cookie;
     60 static u_int64_t lastseenchall;
     61 static u_int64_t key1, key2, oldkey1, oldkey2;
     62 
     63 void
     64 roaming_reply(int type, u_int32_t seq, void *ctxt)
     65 {
     66 	if (type == SSH2_MSG_REQUEST_FAILURE) {
     67 		logit("Server denied roaming");
     68 		return;
     69 	}
     70 	verbose("Roaming enabled");
     71 	roaming_id = packet_get_int();
     72 	cookie = packet_get_int64();
     73 	key1 = oldkey1 = packet_get_int64();
     74 	key2 = oldkey2 = packet_get_int64();
     75 	set_out_buffer_size(packet_get_int() +  get_snd_buf_size());
     76 	roaming_enabled = 1;
     77 }
     78 
     79 void
     80 request_roaming(void)
     81 {
     82 	packet_start(SSH2_MSG_GLOBAL_REQUEST);
     83 	packet_put_cstring(ROAMING_REQUEST);
     84 	packet_put_char(1);
     85 	packet_put_int(get_recv_buf_size());
     86 	packet_send();
     87 	client_register_global_confirm(roaming_reply, NULL);
     88 }
     89 
     90 static void
     91 roaming_auth_required(void)
     92 {
     93 	u_char digest[SHA_DIGEST_LENGTH];
     94 	EVP_MD_CTX md;
     95 	Buffer b;
     96 	const EVP_MD *evp_md = EVP_sha1();
     97 	u_int64_t chall, oldchall;
     98 
     99 	chall = packet_get_int64();
    100 	oldchall = packet_get_int64();
    101 	if (oldchall != lastseenchall) {
    102 		key1 = oldkey1;
    103 		key2 = oldkey2;
    104 	}
    105 	lastseenchall = chall;
    106 
    107 	buffer_init(&b);
    108 	buffer_put_int64(&b, cookie);
    109 	buffer_put_int64(&b, chall);
    110 	EVP_DigestInit(&md, evp_md);
    111 	EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
    112 	EVP_DigestFinal(&md, digest, NULL);
    113 	buffer_free(&b);
    114 
    115 	packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
    116 	packet_put_int64(key1 ^ get_recv_bytes());
    117 	packet_put_raw(digest, sizeof(digest));
    118 	packet_send();
    119 
    120 	oldkey1 = key1;
    121 	oldkey2 = key2;
    122 	calculate_new_key(&key1, cookie, chall);
    123 	calculate_new_key(&key2, cookie, chall);
    124 
    125 	debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
    126 	debug("Sent roaming_auth packet");
    127 }
    128 
    129 int
    130 resume_kex(void)
    131 {
    132 	/*
    133 	 * This should not happen - if the client sends the kex method
    134 	 * resume (at) appgate.com then the kex is done in roaming_resume().
    135 	 */
    136 	return 1;
    137 }
    138 
    139 static int
    140 roaming_resume(void)
    141 {
    142 	u_int64_t recv_bytes;
    143 	char *str = NULL, *kexlist = NULL, *c;
    144 	int i, type;
    145 	int timeout_ms = options.connection_timeout * 1000;
    146 	u_int len;
    147 	u_int32_t rnd = 0;
    148 
    149 	resume_in_progress = 1;
    150 
    151 	/* Exchange banners */
    152 	ssh_exchange_identification(timeout_ms);
    153 	packet_set_nonblocking();
    154 
    155 	/* Send a kexinit message with resume (at) appgate.com as only kex algo */
    156 	packet_start(SSH2_MSG_KEXINIT);
    157 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
    158 		if (i % 4 == 0)
    159 			rnd = arc4random();
    160 		packet_put_char(rnd & 0xff);
    161 		rnd >>= 8;
    162 	}
    163 	packet_put_cstring(KEX_RESUME);
    164 	for (i = 1; i < PROPOSAL_MAX; i++) {
    165 		/* kex algorithm added so start with i=1 and not 0 */
    166 		packet_put_cstring(""); /* Not used when we resume */
    167 	}
    168 	packet_put_char(1); /* first kex_packet follows */
    169 	packet_put_int(0); /* reserved */
    170 	packet_send();
    171 
    172 	/* Assume that resume (at) appgate.com will be accepted */
    173 	packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
    174 	packet_put_int(roaming_id);
    175 	packet_send();
    176 
    177 	/* Read the server's kexinit and check for resume (at) appgate.com */
    178 	if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
    179 		debug("expected kexinit on resume, got %d", type);
    180 		goto fail;
    181 	}
    182 	for (i = 0; i < KEX_COOKIE_LEN; i++)
    183 		(void)packet_get_char();
    184 	kexlist = packet_get_string(&len);
    185 	if (!kexlist
    186 	    || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
    187 		debug("server doesn't allow resume");
    188 		goto fail;
    189 	}
    190 	xfree(str);
    191 	for (i = 1; i < PROPOSAL_MAX; i++) {
    192 		/* kex algorithm taken care of so start with i=1 and not 0 */
    193 		xfree(packet_get_string(&len));
    194 	}
    195 	i = packet_get_char(); /* first_kex_packet_follows */
    196 	if (i && (c = strchr(kexlist, ',')))
    197 		*c = 0;
    198 	if (i && strcmp(kexlist, KEX_RESUME)) {
    199 		debug("server's kex guess (%s) was wrong, skipping", kexlist);
    200 		(void)packet_read(); /* Wrong guess - discard packet */
    201 	}
    202 
    203 	/*
    204 	 * Read the ROAMING_AUTH_REQUIRED challenge from the server and
    205 	 * send ROAMING_AUTH
    206 	 */
    207 	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
    208 		debug("expected roaming_auth_required, got %d", type);
    209 		goto fail;
    210 	}
    211 	roaming_auth_required();
    212 
    213 	/* Read ROAMING_AUTH_OK from the server */
    214 	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
    215 		debug("expected roaming_auth_ok, got %d", type);
    216 		goto fail;
    217 	}
    218 	recv_bytes = packet_get_int64() ^ oldkey2;
    219 	debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
    220 	resend_bytes(packet_get_connection_out(), &recv_bytes);
    221 
    222 	resume_in_progress = 0;
    223 
    224 	session_resumed = 1; /* Tell clientloop */
    225 
    226 	return 0;
    227 
    228 fail:
    229 	if (kexlist)
    230 		xfree(kexlist);
    231 	if (packet_get_connection_in() == packet_get_connection_out())
    232 		close(packet_get_connection_in());
    233 	else {
    234 		close(packet_get_connection_in());
    235 		close(packet_get_connection_out());
    236 	}
    237 	return 1;
    238 }
    239 
    240 int
    241 wait_for_roaming_reconnect(void)
    242 {
    243 	static int reenter_guard = 0;
    244 	int timeout_ms = options.connection_timeout * 1000;
    245 	int c;
    246 
    247 	if (reenter_guard != 0)
    248 		fatal("Server refused resume, roaming timeout may be exceeded");
    249 	reenter_guard = 1;
    250 
    251 	fprintf(stderr, "[connection suspended, press return to resume]");
    252 	fflush(stderr);
    253 	packet_backup_state();
    254 	/* TODO Perhaps we should read from tty here */
    255 	while ((c = fgetc(stdin)) != EOF) {
    256 		if (c == 'Z' - 64) {
    257 			kill(getpid(), SIGTSTP);
    258 			continue;
    259 		}
    260 		if (c != '\n' && c != '\r')
    261 			continue;
    262 
    263 		if (ssh_connect(host, &hostaddr, options.port,
    264 		    options.address_family, 1, &timeout_ms,
    265 		    options.tcp_keep_alive, options.use_privileged_port,
    266 		    options.proxy_command) == 0 && roaming_resume() == 0) {
    267 			packet_restore_state();
    268 			reenter_guard = 0;
    269 			fprintf(stderr, "[connection resumed]\n");
    270 			fflush(stderr);
    271 			return 0;
    272 		}
    273 
    274 		fprintf(stderr, "[reconnect failed, press return to retry]");
    275 		fflush(stderr);
    276 	}
    277 	fprintf(stderr, "[exiting]\n");
    278 	fflush(stderr);
    279 	exit(0);
    280 }
    281