]> andersk Git - openssh.git/blob - roaming_client.c
- (dtucker) [configure.ac misc.c readconf.c servconf.c ssh-keyscan.c] Make
[openssh.git] / roaming_client.c
1 /* $OpenBSD: roaming_client.c,v 1.1 2009/10/24 11:22:37 andreas 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 <sys/queue.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21
22 #include <inttypes.h>
23 #include <signal.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <openssl/crypto.h>
28 #include <openssl/sha.h>
29
30 #include "xmalloc.h"
31 #include "buffer.h"
32 #include "channels.h"
33 #include "cipher.h"
34 #include "dispatch.h"
35 #include "clientloop.h"
36 #include "log.h"
37 #include "match.h"
38 #include "misc.h"
39 #include "packet.h"
40 #include "ssh.h"
41 #include "key.h"
42 #include "kex.h"
43 #include "readconf.h"
44 #include "roaming.h"
45 #include "ssh2.h"
46 #include "sshconnect.h"
47
48 /* import */
49 extern Options options;
50 extern char *host;
51 extern struct sockaddr_storage hostaddr;
52 extern int session_resumed;
53
54 static u_int32_t roaming_id;
55 static u_int64_t cookie;
56 static u_int64_t lastseenchall;
57 static u_int64_t key1, key2, oldkey1, oldkey2;
58
59 void
60 roaming_reply(int type, u_int32_t seq, void *ctxt)
61 {
62         if (type == SSH2_MSG_REQUEST_FAILURE) {
63                 logit("Server denied roaming");
64                 return;
65         }
66         verbose("Roaming enabled");
67         roaming_id = packet_get_int();
68         cookie = packet_get_int64();
69         key1 = oldkey1 = packet_get_int64();
70         key2 = oldkey2 = packet_get_int64();
71         set_out_buffer_size(packet_get_int() +  get_snd_buf_size());
72         roaming_enabled = 1;
73 }
74
75 void
76 request_roaming(void)
77 {
78         packet_start(SSH2_MSG_GLOBAL_REQUEST);
79         packet_put_cstring(ROAMING_REQUEST);
80         packet_put_char(1);
81         packet_put_int(get_recv_buf_size());
82         packet_send();
83         client_register_global_confirm(roaming_reply, NULL);
84 }
85
86 static void
87 roaming_auth_required(void)
88 {
89         u_char digest[SHA_DIGEST_LENGTH];
90         EVP_MD_CTX md;
91         Buffer b;
92         const EVP_MD *evp_md = EVP_sha1();
93         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);
106         EVP_DigestInit(&md, evp_md);
107         EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
108         EVP_DigestFinal(&md, digest, NULL);
109         buffer_free(&b);
110
111         packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
112         packet_put_int64(key1 ^ get_recv_bytes());
113         packet_put_raw(digest, sizeof(digest));
114         packet_send();
115
116         oldkey1 = key1;
117         oldkey2 = key2;
118         calculate_new_key(&key1, cookie, chall);
119         calculate_new_key(&key2, cookie, chall);
120
121         debug("Received %" PRIu64 " bytes", get_recv_bytes());
122         debug("Sent roaming_auth packet");
123 }
124
125 int
126 resume_kex(void)
127 {
128         /*
129          * This should not happen - if the client sends the kex method
130          * resume@appgate.com then the kex is done in roaming_resume().
131          */
132         return 1;
133 }
134
135 static int
136 roaming_resume(void)
137 {
138         u_int64_t recv_bytes;
139         char *str = NULL, *kexlist = NULL, *c;
140         int i, type;
141         int timeout_ms = options.connection_timeout * 1000;
142         u_int len;
143         u_int32_t rnd = 0;
144
145         resume_in_progress = 1;
146
147         /* Exchange banners */
148         ssh_exchange_identification(timeout_ms);
149         packet_set_nonblocking();
150
151         /* Send a kexinit message with resume@appgate.com as only kex algo */
152         packet_start(SSH2_MSG_KEXINIT);
153         for (i = 0; i < KEX_COOKIE_LEN; i++) {
154                 if (i % 4 == 0)
155                         rnd = arc4random();
156                 packet_put_char(rnd & 0xff);
157                 rnd >>= 8;
158         }
159         packet_put_cstring(KEX_RESUME);
160         for (i = 1; i < PROPOSAL_MAX; i++) {
161                 /* kex algorithm added so start with i=1 and not 0 */
162                 packet_put_cstring(""); /* Not used when we resume */
163         }
164         packet_put_char(1); /* first kex_packet follows */
165         packet_put_int(0); /* reserved */
166         packet_send();
167
168         /* Assume that resume@appgate.com will be accepted */
169         packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
170         packet_put_int(roaming_id);
171         packet_send();
172
173         /* Read the server's kexinit and check for resume@appgate.com */
174         if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
175                 debug("expected kexinit on resume, got %d", type);
176                 goto fail;
177         }
178         for (i = 0; i < KEX_COOKIE_LEN; i++)
179                 (void)packet_get_char();
180         kexlist = packet_get_string(&len);
181         if (!kexlist
182             || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
183                 debug("server doesn't allow resume");
184                 goto fail;
185         }
186         xfree(str);
187         for (i = 1; i < PROPOSAL_MAX; i++) {
188                 /* kex algorithm taken care of so start with i=1 and not 0 */
189                 xfree(packet_get_string(&len));
190         }
191         i = packet_get_char(); /* first_kex_packet_follows */
192         if (i && (c = strchr(kexlist, ',')))
193                 *c = 0;
194         if (i && strcmp(kexlist, KEX_RESUME)) {
195                 debug("server's kex guess (%s) was wrong, skipping", kexlist);
196                 (void)packet_read(); /* Wrong guess - discard packet */
197         }
198
199         /*
200          * Read the ROAMING_AUTH_REQUIRED challenge from the server and
201          * send ROAMING_AUTH
202          */
203         if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
204                 debug("expected roaming_auth_required, got %d", type);
205                 goto fail;
206         }
207         roaming_auth_required();
208
209         /* Read ROAMING_AUTH_OK from the server */
210         if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
211                 debug("expected roaming_auth_ok, got %d", type);
212                 goto fail;
213         }
214         recv_bytes = packet_get_int64() ^ oldkey2;
215         debug("Peer received %" PRIu64 " bytes", recv_bytes);
216         resend_bytes(packet_get_connection_out(), &recv_bytes);
217
218         resume_in_progress = 0;
219
220         session_resumed = 1; /* Tell clientloop */
221
222         return 0;
223
224 fail:
225         if (kexlist)
226                 xfree(kexlist);
227         if (packet_get_connection_in() == packet_get_connection_out())
228                 close(packet_get_connection_in());
229         else {
230                 close(packet_get_connection_in());
231                 close(packet_get_connection_out());
232         }
233         return 1;
234 }
235
236 int
237 wait_for_roaming_reconnect(void)
238 {
239         static int reenter_guard = 0;
240         int timeout_ms = options.connection_timeout * 1000;
241         int c;
242
243         if (reenter_guard != 0)
244                 fatal("Server refused resume, roaming timeout may be exceeded");
245         reenter_guard = 1;
246
247         fprintf(stderr, "[connection suspended, press return to resume]");
248         fflush(stderr);
249         packet_backup_state();
250         /* TODO Perhaps we should read from tty here */
251         while ((c = fgetc(stdin)) != EOF) {
252                 if (c == 'Z' - 64) {
253                         kill(getpid(), SIGTSTP);
254                         continue;
255                 }
256                 if (c != '\n' && c != '\r')
257                         continue;
258
259                 if (ssh_connect(host, &hostaddr, options.port,
260                     options.address_family, 1, &timeout_ms,
261                     options.tcp_keep_alive, options.use_privileged_port,
262                     options.proxy_command) == 0 && roaming_resume() == 0) {
263                         packet_restore_state();
264                         reenter_guard = 0;
265                         fprintf(stderr, "[connection resumed]\n");
266                         fflush(stderr);
267                         return 0;
268                 }
269
270                 fprintf(stderr, "[reconnect failed, press return to retry]");
271                 fflush(stderr);
272         }
273         fprintf(stderr, "[exiting]\n");
274         fflush(stderr);
275         exit(0);
276 }
This page took 0.062476 seconds and 5 git commands to generate.