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