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