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