1 /* $OpenBSD: roaming_client.c,v 1.2 2010/01/09 05:17:00 dtucker 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 <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"
53 extern Options options
;
55 extern struct sockaddr_storage hostaddr
;
56 extern int session_resumed
;
58 static u_int32_t roaming_id
;
59 static u_int64_t cookie
;
60 static u_int64_t lastseenchall
;
61 static u_int64_t key1
, key2
, oldkey1
, oldkey2
;
64 roaming_reply(int type
, u_int32_t seq
, void *ctxt
)
66 if (type
== SSH2_MSG_REQUEST_FAILURE
) {
67 logit("Server denied roaming");
70 verbose("Roaming enabled");
71 roaming_id
= packet_get_int();
72 cookie
= packet_get_int64();
73 key1
= oldkey1
= packet_get_int64();
74 key2
= oldkey2
= packet_get_int64();
75 set_out_buffer_size(packet_get_int() + get_snd_buf_size());
82 packet_start(SSH2_MSG_GLOBAL_REQUEST
);
83 packet_put_cstring(ROAMING_REQUEST
);
85 packet_put_int(get_recv_buf_size());
87 client_register_global_confirm(roaming_reply
, NULL
);
91 roaming_auth_required(void)
93 u_char digest
[SHA_DIGEST_LENGTH
];
96 const EVP_MD
*evp_md
= EVP_sha1();
97 u_int64_t chall
, oldchall
;
99 chall
= packet_get_int64();
100 oldchall
= packet_get_int64();
101 if (oldchall
!= lastseenchall
) {
105 lastseenchall
= chall
;
108 buffer_put_int64(&b
, cookie
);
109 buffer_put_int64(&b
, chall
);
110 EVP_DigestInit(&md
, evp_md
);
111 EVP_DigestUpdate(&md
, buffer_ptr(&b
), buffer_len(&b
));
112 EVP_DigestFinal(&md
, digest
, NULL
);
115 packet_start(SSH2_MSG_KEX_ROAMING_AUTH
);
116 packet_put_int64(key1
^ get_recv_bytes());
117 packet_put_raw(digest
, sizeof(digest
));
122 calculate_new_key(&key1
, cookie
, chall
);
123 calculate_new_key(&key2
, cookie
, chall
);
125 debug("Received %llu bytes", (long long unsigned)get_recv_bytes());
126 debug("Sent roaming_auth packet");
133 * This should not happen - if the client sends the kex method
134 * resume@appgate.com then the kex is done in roaming_resume().
142 u_int64_t recv_bytes
;
143 char *str
= NULL
, *kexlist
= NULL
, *c
;
145 int timeout_ms
= options
.connection_timeout
* 1000;
149 resume_in_progress
= 1;
151 /* Exchange banners */
152 ssh_exchange_identification(timeout_ms
);
153 packet_set_nonblocking();
155 /* Send a kexinit message with resume@appgate.com as only kex algo */
156 packet_start(SSH2_MSG_KEXINIT
);
157 for (i
= 0; i
< KEX_COOKIE_LEN
; i
++) {
160 packet_put_char(rnd
& 0xff);
163 packet_put_cstring(KEX_RESUME
);
164 for (i
= 1; i
< PROPOSAL_MAX
; i
++) {
165 /* kex algorithm added so start with i=1 and not 0 */
166 packet_put_cstring(""); /* Not used when we resume */
168 packet_put_char(1); /* first kex_packet follows */
169 packet_put_int(0); /* reserved */
172 /* Assume that resume@appgate.com will be accepted */
173 packet_start(SSH2_MSG_KEX_ROAMING_RESUME
);
174 packet_put_int(roaming_id
);
177 /* Read the server's kexinit and check for resume@appgate.com */
178 if ((type
= packet_read()) != SSH2_MSG_KEXINIT
) {
179 debug("expected kexinit on resume, got %d", type
);
182 for (i
= 0; i
< KEX_COOKIE_LEN
; i
++)
183 (void)packet_get_char();
184 kexlist
= packet_get_string(&len
);
186 || (str
= match_list(KEX_RESUME
, kexlist
, NULL
)) == NULL
) {
187 debug("server doesn't allow resume");
191 for (i
= 1; i
< PROPOSAL_MAX
; i
++) {
192 /* kex algorithm taken care of so start with i=1 and not 0 */
193 xfree(packet_get_string(&len
));
195 i
= packet_get_char(); /* first_kex_packet_follows */
196 if (i
&& (c
= strchr(kexlist
, ',')))
198 if (i
&& strcmp(kexlist
, KEX_RESUME
)) {
199 debug("server's kex guess (%s) was wrong, skipping", kexlist
);
200 (void)packet_read(); /* Wrong guess - discard packet */
204 * Read the ROAMING_AUTH_REQUIRED challenge from the server and
207 if ((type
= packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED
) {
208 debug("expected roaming_auth_required, got %d", type
);
211 roaming_auth_required();
213 /* Read ROAMING_AUTH_OK from the server */
214 if ((type
= packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK
) {
215 debug("expected roaming_auth_ok, got %d", type
);
218 recv_bytes
= packet_get_int64() ^ oldkey2
;
219 debug("Peer received %llu bytes", (long long unsigned)recv_bytes
);
220 resend_bytes(packet_get_connection_out(), &recv_bytes
);
222 resume_in_progress
= 0;
224 session_resumed
= 1; /* Tell clientloop */
231 if (packet_get_connection_in() == packet_get_connection_out())
232 close(packet_get_connection_in());
234 close(packet_get_connection_in());
235 close(packet_get_connection_out());
241 wait_for_roaming_reconnect(void)
243 static int reenter_guard
= 0;
244 int timeout_ms
= options
.connection_timeout
* 1000;
247 if (reenter_guard
!= 0)
248 fatal("Server refused resume, roaming timeout may be exceeded");
251 fprintf(stderr
, "[connection suspended, press return to resume]");
253 packet_backup_state();
254 /* TODO Perhaps we should read from tty here */
255 while ((c
= fgetc(stdin
)) != EOF
) {
257 kill(getpid(), SIGTSTP
);
260 if (c
!= '\n' && c
!= '\r')
263 if (ssh_connect(host
, &hostaddr
, options
.port
,
264 options
.address_family
, 1, &timeout_ms
,
265 options
.tcp_keep_alive
, options
.use_privileged_port
,
266 options
.proxy_command
) == 0 && roaming_resume() == 0) {
267 packet_restore_state();
269 fprintf(stderr
, "[connection resumed]\n");
274 fprintf(stderr
, "[reconnect failed, press return to retry]");
277 fprintf(stderr
, "[exiting]\n");