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
roaming_reply(int type,u_int32_t seq,void * ctxt)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
request_roaming(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
roaming_auth_required(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
resume_kex(void)122 resume_kex(void)
123 {
124 	/*
125 	 * This should not happen - if the client sends the kex method
126 	 * resume@appgate.com then the kex is done in roaming_resume().
127 	 */
128 	return 1;
129 }
130 
131 static int
roaming_resume(void)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@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@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@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
wait_for_roaming_reconnect(void)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