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