blob: 81c496827ecd01340e949e4f0f9c59ef188adcf4 [file] [log] [blame]
Darren Tuckera627d422013-06-02 07:31:17 +10001/* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */
Darren Tucker8cbd4032010-01-08 19:13:25 +11002/*
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
Darren Tuckercb5a1b62010-01-08 20:09:01 +110018#include "includes.h"
19
Tim Ricef3775672010-01-16 16:48:39 -080020#include "openbsd-compat/sys-queue.h"
Darren Tucker8cbd4032010-01-08 19:13:25 +110021#include <sys/types.h>
22#include <sys/socket.h>
23
Darren Tucker11b5c072010-01-09 16:40:48 +110024#ifdef HAVE_INTTYPES_H
Darren Tucker8cbd4032010-01-08 19:13:25 +110025#include <inttypes.h>
Darren Tucker11b5c072010-01-09 16:40:48 +110026#endif
Darren Tucker8cbd4032010-01-08 19:13:25 +110027#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 */
53extern Options options;
54extern char *host;
55extern struct sockaddr_storage hostaddr;
56extern int session_resumed;
57
58static u_int32_t roaming_id;
59static u_int64_t cookie;
60static u_int64_t lastseenchall;
61static u_int64_t key1, key2, oldkey1, oldkey2;
62
63void
64roaming_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();
Damien Miller8ed4de82011-12-19 10:52:50 +110075 set_out_buffer_size(packet_get_int() + get_snd_buf_size());
Darren Tucker8cbd4032010-01-08 19:13:25 +110076 roaming_enabled = 1;
77}
78
79void
80request_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
90static void
91roaming_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
Tim Ricef3775672010-01-16 16:48:39 -0800125 debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
Darren Tucker8cbd4032010-01-08 19:13:25 +1100126 debug("Sent roaming_auth packet");
127}
128
129int
130resume_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
139static int
140roaming_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 }
Darren Tuckera627d422013-06-02 07:31:17 +1000190 free(str);
Darren Tucker8cbd4032010-01-08 19:13:25 +1100191 for (i = 1; i < PROPOSAL_MAX; i++) {
192 /* kex algorithm taken care of so start with i=1 and not 0 */
Darren Tuckera627d422013-06-02 07:31:17 +1000193 free(packet_get_string(&len));
Darren Tucker8cbd4032010-01-08 19:13:25 +1100194 }
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;
Tim Ricef3775672010-01-16 16:48:39 -0800219 debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
Darren Tucker8cbd4032010-01-08 19:13:25 +1100220 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
228fail:
Darren Tuckera627d422013-06-02 07:31:17 +1000229 free(kexlist);
Darren Tucker8cbd4032010-01-08 19:13:25 +1100230 if (packet_get_connection_in() == packet_get_connection_out())
231 close(packet_get_connection_in());
232 else {
233 close(packet_get_connection_in());
234 close(packet_get_connection_out());
235 }
236 return 1;
237}
238
239int
240wait_for_roaming_reconnect(void)
241{
242 static int reenter_guard = 0;
243 int timeout_ms = options.connection_timeout * 1000;
244 int c;
245
246 if (reenter_guard != 0)
247 fatal("Server refused resume, roaming timeout may be exceeded");
248 reenter_guard = 1;
249
250 fprintf(stderr, "[connection suspended, press return to resume]");
251 fflush(stderr);
252 packet_backup_state();
253 /* TODO Perhaps we should read from tty here */
254 while ((c = fgetc(stdin)) != EOF) {
255 if (c == 'Z' - 64) {
256 kill(getpid(), SIGTSTP);
257 continue;
258 }
259 if (c != '\n' && c != '\r')
260 continue;
261
262 if (ssh_connect(host, &hostaddr, options.port,
263 options.address_family, 1, &timeout_ms,
264 options.tcp_keep_alive, options.use_privileged_port,
265 options.proxy_command) == 0 && roaming_resume() == 0) {
266 packet_restore_state();
267 reenter_guard = 0;
268 fprintf(stderr, "[connection resumed]\n");
269 fflush(stderr);
270 return 0;
271 }
272
273 fprintf(stderr, "[reconnect failed, press return to retry]");
274 fflush(stderr);
275 }
276 fprintf(stderr, "[exiting]\n");
277 fflush(stderr);
278 exit(0);
279}