Home | History | Annotate | Download | only in openssh
      1 /* $OpenBSD: roaming_client.c,v 1.9 2015/01/27 12:54:06 okan 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 #include <signal.h>
     25 #include <string.h>
     26 #include <unistd.h>
     27 
     28 #include "xmalloc.h"
     29 #include "buffer.h"
     30 #include "channels.h"
     31 #include "cipher.h"
     32 #include "dispatch.h"
     33 #include "clientloop.h"
     34 #include "log.h"
     35 #include "match.h"
     36 #include "misc.h"
     37 #include "packet.h"
     38 #include "ssh.h"
     39 #include "key.h"
     40 #include "kex.h"
     41 #include "readconf.h"
     42 #include "roaming.h"
     43 #include "ssh2.h"
     44 #include "sshconnect.h"
     45 #include "digest.h"
     46 
     47 /* import */
     48 extern Options options;
     49 extern char *host;
     50 extern struct sockaddr_storage hostaddr;
     51 extern int session_resumed;
     52 
     53 static u_int32_t roaming_id;
     54 static u_int64_t cookie;
     55 static u_int64_t lastseenchall;
     56 static u_int64_t key1, key2, oldkey1, oldkey2;
     57 
     58 void
     59 roaming_reply(int type, u_int32_t seq, void *ctxt)
     60 {
     61 	if (type == SSH2_MSG_REQUEST_FAILURE) {
     62 		logit("Server denied roaming");
     63 		return;
     64 	}
     65 	verbose("Roaming enabled");
     66 	roaming_id = packet_get_int();
     67 	cookie = packet_get_int64();
     68 	key1 = oldkey1 = packet_get_int64();
     69 	key2 = oldkey2 = packet_get_int64();
     70 	set_out_buffer_size(packet_get_int() + get_snd_buf_size());
     71 	roaming_enabled = 1;
     72 }
     73 
     74 void
     75 request_roaming(void)
     76 {
     77 	packet_start(SSH2_MSG_GLOBAL_REQUEST);
     78 	packet_put_cstring(ROAMING_REQUEST);
     79 	packet_put_char(1);
     80 	packet_put_int(get_recv_buf_size());
     81 	packet_send();
     82 	client_register_global_confirm(roaming_reply, NULL);
     83 }
     84 
     85 static void
     86 roaming_auth_required(void)
     87 {
     88 	u_char digest[SSH_DIGEST_MAX_LENGTH];
     89 	Buffer b;
     90 	u_int64_t chall, oldchall;
     91 
     92 	chall = packet_get_int64();
     93 	oldchall = packet_get_int64();
     94 	if (oldchall != lastseenchall) {
     95 		key1 = oldkey1;
     96 		key2 = oldkey2;
     97 	}
     98 	lastseenchall = chall;
     99 
    100 	buffer_init(&b);
    101 	buffer_put_int64(&b, cookie);
    102 	buffer_put_int64(&b, chall);
    103 	if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, digest, sizeof(digest)) != 0)
    104 		fatal("%s: ssh_digest_buffer failed", __func__);
    105 	buffer_free(&b);
    106 
    107 	packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
    108 	packet_put_int64(key1 ^ get_recv_bytes());
    109 	packet_put_raw(digest, ssh_digest_bytes(SSH_DIGEST_SHA1));
    110 	packet_send();
    111 
    112 	oldkey1 = key1;
    113 	oldkey2 = key2;
    114 	calculate_new_key(&key1, cookie, chall);
    115 	calculate_new_key(&key2, cookie, chall);
    116 
    117 	debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
    118 	debug("Sent roaming_auth packet");
    119 }
    120 
    121 int
    122 resume_kex(void)
    123 {
    124 	/*
    125 	 * This should not happen - if the client sends the kex method
    126 	 * resume (at) appgate.com then the kex is done in roaming_resume().
    127 	 */
    128 	return 1;
    129 }
    130 
    131 static int
    132 roaming_resume(void)
    133 {
    134 	u_int64_t recv_bytes;
    135 	char *str = NULL, *kexlist = NULL, *c;
    136 	int i, type;
    137 	int timeout_ms = options.connection_timeout * 1000;
    138 	u_int len;
    139 	u_int32_t rnd = 0;
    140 
    141 	resume_in_progress = 1;
    142 
    143 	/* Exchange banners */
    144 	ssh_exchange_identification(timeout_ms);
    145 	packet_set_nonblocking();
    146 
    147 	/* Send a kexinit message with resume (at) appgate.com as only kex algo */
    148 	packet_start(SSH2_MSG_KEXINIT);
    149 	for (i = 0; i < KEX_COOKIE_LEN; i++) {
    150 		if (i % 4 == 0)
    151 			rnd = arc4random();
    152 		packet_put_char(rnd & 0xff);
    153 		rnd >>= 8;
    154 	}
    155 	packet_put_cstring(KEX_RESUME);
    156 	for (i = 1; i < PROPOSAL_MAX; i++) {
    157 		/* kex algorithm added so start with i=1 and not 0 */
    158 		packet_put_cstring(""); /* Not used when we resume */
    159 	}
    160 	packet_put_char(1); /* first kex_packet follows */
    161 	packet_put_int(0); /* reserved */
    162 	packet_send();
    163 
    164 	/* Assume that resume (at) appgate.com will be accepted */
    165 	packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
    166 	packet_put_int(roaming_id);
    167 	packet_send();
    168 
    169 	/* Read the server's kexinit and check for resume (at) appgate.com */
    170 	if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
    171 		debug("expected kexinit on resume, got %d", type);
    172 		goto fail;
    173 	}
    174 	for (i = 0; i < KEX_COOKIE_LEN; i++)
    175 		(void)packet_get_char();
    176 	kexlist = packet_get_string(&len);
    177 	if (!kexlist
    178 	    || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
    179 		debug("server doesn't allow resume");
    180 		goto fail;
    181 	}
    182 	free(str);
    183 	for (i = 1; i < PROPOSAL_MAX; i++) {
    184 		/* kex algorithm taken care of so start with i=1 and not 0 */
    185 		free(packet_get_string(&len));
    186 	}
    187 	i = packet_get_char(); /* first_kex_packet_follows */
    188 	if (i && (c = strchr(kexlist, ',')))
    189 		*c = 0;
    190 	if (i && strcmp(kexlist, KEX_RESUME)) {
    191 		debug("server's kex guess (%s) was wrong, skipping", kexlist);
    192 		(void)packet_read(); /* Wrong guess - discard packet */
    193 	}
    194 
    195 	/*
    196 	 * Read the ROAMING_AUTH_REQUIRED challenge from the server and
    197 	 * send ROAMING_AUTH
    198 	 */
    199 	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
    200 		debug("expected roaming_auth_required, got %d", type);
    201 		goto fail;
    202 	}
    203 	roaming_auth_required();
    204 
    205 	/* Read ROAMING_AUTH_OK from the server */
    206 	if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
    207 		debug("expected roaming_auth_ok, got %d", type);
    208 		goto fail;
    209 	}
    210 	recv_bytes = packet_get_int64() ^ oldkey2;
    211 	debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
    212 	resend_bytes(packet_get_connection_out(), &recv_bytes);
    213 
    214 	resume_in_progress = 0;
    215 
    216 	session_resumed = 1; /* Tell clientloop */
    217 
    218 	return 0;
    219 
    220 fail:
    221 	free(kexlist);
    222 	if (packet_get_connection_in() == packet_get_connection_out())
    223 		close(packet_get_connection_in());
    224 	else {
    225 		close(packet_get_connection_in());
    226 		close(packet_get_connection_out());
    227 	}
    228 	return 1;
    229 }
    230 
    231 int
    232 wait_for_roaming_reconnect(void)
    233 {
    234 	static int reenter_guard = 0;
    235 	int timeout_ms = options.connection_timeout * 1000;
    236 	int c;
    237 
    238 	if (reenter_guard != 0)
    239 		fatal("Server refused resume, roaming timeout may be exceeded");
    240 	reenter_guard = 1;
    241 
    242 	fprintf(stderr, "[connection suspended, press return to resume]");
    243 	fflush(stderr);
    244 	packet_backup_state();
    245 	/* TODO Perhaps we should read from tty here */
    246 	while ((c = fgetc(stdin)) != EOF) {
    247 		if (c == 'Z' - 64) {
    248 			kill(getpid(), SIGTSTP);
    249 			continue;
    250 		}
    251 		if (c != '\n' && c != '\r')
    252 			continue;
    253 
    254 		if (ssh_connect(host, NULL, &hostaddr, options.port,
    255 		    options.address_family, 1, &timeout_ms,
    256 		    options.tcp_keep_alive, options.use_privileged_port) == 0 &&
    257 		    roaming_resume() == 0) {
    258 			packet_restore_state();
    259 			reenter_guard = 0;
    260 			fprintf(stderr, "[connection resumed]\n");
    261 			fflush(stderr);
    262 			return 0;
    263 		}
    264 
    265 		fprintf(stderr, "[reconnect failed, press return to retry]");
    266 		fflush(stderr);
    267 	}
    268 	fprintf(stderr, "[exiting]\n");
    269 	fflush(stderr);
    270 	exit(0);
    271 }
    272