1 /* $OpenBSD: roaming_client.c,v 1.7 2014/01/09 23:20:00 djm Exp $ */
3 * Copyright (c) 2004-2009 AppGate Network Security AB
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.
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.
20 #include "openbsd-compat/sys-queue.h"
21 #include <sys/types.h>
22 #include <sys/socket.h>
24 #ifdef HAVE_INTTYPES_H
31 #include <openssl/crypto.h>
32 #include <openssl/sha.h>
39 #include "clientloop.h"
50 #include "sshconnect.h"
54 extern Options options;
56 extern struct sockaddr_storage hostaddr;
57 extern int session_resumed;
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;
65 roaming_reply(int type, u_int32_t seq, void *ctxt)
67 if (type == SSH2_MSG_REQUEST_FAILURE) {
68 logit("Server denied roaming");
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());
83 packet_start(SSH2_MSG_GLOBAL_REQUEST);
84 packet_put_cstring(ROAMING_REQUEST);
86 packet_put_int(get_recv_buf_size());
88 client_register_global_confirm(roaming_reply, NULL);
92 roaming_auth_required(void)
94 u_char digest[SSH_DIGEST_MAX_LENGTH];
96 u_int64_t chall, oldchall;
98 chall = packet_get_int64();
99 oldchall = packet_get_int64();
100 if (oldchall != lastseenchall) {
104 lastseenchall = chall;
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__);
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));
120 calculate_new_key(&key1, cookie, chall);
121 calculate_new_key(&key2, cookie, chall);
123 debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
124 debug("Sent roaming_auth packet");
131 * This should not happen - if the client sends the kex method
132 * resume@appgate.com then the kex is done in roaming_resume().
140 u_int64_t recv_bytes;
141 char *str = NULL, *kexlist = NULL, *c;
143 int timeout_ms = options.connection_timeout * 1000;
147 resume_in_progress = 1;
149 /* Exchange banners */
150 ssh_exchange_identification(timeout_ms);
151 packet_set_nonblocking();
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++) {
158 packet_put_char(rnd & 0xff);
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 */
166 packet_put_char(1); /* first kex_packet follows */
167 packet_put_int(0); /* reserved */
170 /* Assume that resume@appgate.com will be accepted */
171 packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
172 packet_put_int(roaming_id);
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);
180 for (i = 0; i < KEX_COOKIE_LEN; i++)
181 (void)packet_get_char();
182 kexlist = packet_get_string(&len);
184 || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
185 debug("server doesn't allow resume");
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));
193 i = packet_get_char(); /* first_kex_packet_follows */
194 if (i && (c = strchr(kexlist, ',')))
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 */
202 * Read the ROAMING_AUTH_REQUIRED challenge from the server and
205 if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
206 debug("expected roaming_auth_required, got %d", type);
209 roaming_auth_required();
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);
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);
220 resume_in_progress = 0;
222 session_resumed = 1; /* Tell clientloop */
228 if (packet_get_connection_in() == packet_get_connection_out())
229 close(packet_get_connection_in());
231 close(packet_get_connection_in());
232 close(packet_get_connection_out());
238 wait_for_roaming_reconnect(void)
240 static int reenter_guard = 0;
241 int timeout_ms = options.connection_timeout * 1000;
244 if (reenter_guard != 0)
245 fatal("Server refused resume, roaming timeout may be exceeded");
248 fprintf(stderr, "[connection suspended, press return to resume]");
250 packet_backup_state();
251 /* TODO Perhaps we should read from tty here */
252 while ((c = fgetc(stdin)) != EOF) {
254 kill(getpid(), SIGTSTP);
257 if (c != '\n' && c != '\r')
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();
266 fprintf(stderr, "[connection resumed]\n");
271 fprintf(stderr, "[reconnect failed, press return to retry]");
274 fprintf(stderr, "[exiting]\n");