Makefile_upgrade.inc: Remove /usr/share/libg++
[dragonfly.git] / crypto / openssh / roaming_client.c
1 /* $OpenBSD: roaming_client.c,v 1.8 2014/04/29 18:01:49 markus 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 "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"
48 #include "digest.h"
49
50 /* import */
51 extern Options options;
52 extern char *host;
53 extern struct sockaddr_storage hostaddr;
54 extern int session_resumed;
55
56 static u_int32_t roaming_id;
57 static u_int64_t cookie;
58 static u_int64_t lastseenchall;
59 static u_int64_t key1, key2, oldkey1, oldkey2;
60
61 void
62 roaming_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();
73         set_out_buffer_size(packet_get_int() + get_snd_buf_size());
74         roaming_enabled = 1;
75 }
76
77 void
78 request_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
88 static void
89 roaming_auth_required(void)
90 {
91         u_char digest[SSH_DIGEST_MAX_LENGTH];
92         Buffer b;
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         if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, digest, sizeof(digest)) != 0)
107                 fatal("%s: ssh_digest_buffer failed", __func__);
108         buffer_free(&b);
109
110         packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
111         packet_put_int64(key1 ^ get_recv_bytes());
112         packet_put_raw(digest, ssh_digest_bytes(SSH_DIGEST_SHA1));
113         packet_send();
114
115         oldkey1 = key1;
116         oldkey2 = key2;
117         calculate_new_key(&key1, cookie, chall);
118         calculate_new_key(&key2, cookie, chall);
119
120         debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
121         debug("Sent roaming_auth packet");
122 }
123
124 int
125 resume_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
134 static int
135 roaming_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         }
185         free(str);
186         for (i = 1; i < PROPOSAL_MAX; i++) {
187                 /* kex algorithm taken care of so start with i=1 and not 0 */
188                 free(packet_get_string(&len));
189         }
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;
214         debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
215         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
223 fail:
224         free(kexlist);
225         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
234 int
235 wait_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
257                 if (ssh_connect(host, NULL, &hostaddr, options.port,
258                     options.address_family, 1, &timeout_ms,
259                     options.tcp_keep_alive, options.use_privileged_port) == 0 &&
260                     roaming_resume() == 0) {
261                         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 }