• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
roaming_reply(int type,u_int32_t seq,void * ctxt)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
request_roaming(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
roaming_auth_required(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
resume_kex(void)130 resume_kex(void)
131 {
132 	/*
133 	 * This should not happen - if the client sends the kex method
134 	 * resume@appgate.com then the kex is done in roaming_resume().
135 	 */
136 	return 1;
137 }
138 
139 static int
roaming_resume(void)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@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@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@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
wait_for_roaming_reconnect(void)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