1/* $NetBSD: roaming_client.c,v 1.2 2010/11/21 18:59:04 adam Exp $ */ 2/* $OpenBSD: roaming_client.c,v 1.3 2010/01/18 01:50:27 dtucker Exp $ */ 3/* 4 * Copyright (c) 2004-2009 AppGate Network Security AB 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18#include "includes.h" 19__RCSID("$NetBSD: roaming_client.c,v 1.2 2010/11/21 18:59:04 adam Exp $"); 20 21#include <sys/queue.h> 22#include <sys/types.h> 23#include <sys/socket.h> 24 25#include <inttypes.h> 26#include <signal.h> 27#include <string.h> 28#include <unistd.h> 29 30#include <openssl/crypto.h> 31#include <openssl/sha.h> 32 33#include "xmalloc.h" 34#include "buffer.h" 35#include "channels.h" 36#include "cipher.h" 37#include "dispatch.h" 38#include "clientloop.h" 39#include "log.h" 40#include "match.h" 41#include "misc.h" 42#include "packet.h" 43#include "ssh.h" 44#include "key.h" 45#include "kex.h" 46#include "readconf.h" 47#include "roaming.h" 48#include "ssh2.h" 49#include "sshconnect.h" 50 51/* import */ 52extern Options options; 53extern char *host; 54extern struct sockaddr_storage hostaddr; 55extern int session_resumed; 56 57static u_int32_t roaming_id; 58static u_int64_t cookie; 59static u_int64_t lastseenchall; 60static u_int64_t key1, key2, oldkey1, oldkey2; 61 62void 63roaming_reply(int type, u_int32_t seq, void *ctxt) 64{ 65 if (type == SSH2_MSG_REQUEST_FAILURE) { 66 logit("Server denied roaming"); 67 return; 68 } 69 verbose("Roaming enabled"); 70 roaming_id = packet_get_int(); 71 cookie = packet_get_int64(); 72 key1 = oldkey1 = packet_get_int64(); 73 key2 = oldkey2 = packet_get_int64(); 74 set_out_buffer_size(packet_get_int() + get_snd_buf_size()); 75 roaming_enabled = 1; 76} 77 78void 79request_roaming(void) 80{ 81 packet_start(SSH2_MSG_GLOBAL_REQUEST); 82 packet_put_cstring(ROAMING_REQUEST); 83 packet_put_char(1); 84 packet_put_int(get_recv_buf_size()); 85 packet_send(); 86 client_register_global_confirm(roaming_reply, NULL); 87} 88 89static void 90roaming_auth_required(void) 91{ 92 u_char digest[SHA_DIGEST_LENGTH]; 93 EVP_MD_CTX md; 94 Buffer b; 95 const EVP_MD *evp_md = EVP_sha1(); 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 EVP_DigestInit(&md, evp_md); 110 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 111 EVP_DigestFinal(&md, digest, NULL); 112 buffer_free(&b); 113 114 packet_start(SSH2_MSG_KEX_ROAMING_AUTH); 115 packet_put_int64(key1 ^ get_recv_bytes()); 116 packet_put_raw(digest, sizeof(digest)); 117 packet_send(); 118 119 oldkey1 = key1; 120 oldkey2 = key2; 121 calculate_new_key(&key1, cookie, chall); 122 calculate_new_key(&key2, cookie, chall); 123 124 debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); 125 debug("Sent roaming_auth packet"); 126} 127 128int 129resume_kex(void) 130{ 131 /* 132 * This should not happen - if the client sends the kex method 133 * resume@appgate.com then the kex is done in roaming_resume(). 134 */ 135 return 1; 136} 137 138static int 139roaming_resume(void) 140{ 141 u_int64_t recv_bytes; 142 char *str = NULL, *kexlist = NULL, *c; 143 int i, type; 144 int timeout_ms = options.connection_timeout * 1000; 145 u_int len; 146 u_int32_t rnd = 0; 147 148 resume_in_progress = 1; 149 150 /* Exchange banners */ 151 ssh_exchange_identification(timeout_ms); 152 packet_set_nonblocking(); 153 154 /* Send a kexinit message with resume@appgate.com as only kex algo */ 155 packet_start(SSH2_MSG_KEXINIT); 156 for (i = 0; i < KEX_COOKIE_LEN; i++) { 157 if (i % 4 == 0) 158 rnd = arc4random(); 159 packet_put_char(rnd & 0xff); 160 rnd >>= 8; 161 } 162 packet_put_cstring(KEX_RESUME); 163 for (i = 1; i < PROPOSAL_MAX; i++) { 164 /* kex algorithm added so start with i=1 and not 0 */ 165 packet_put_cstring(""); /* Not used when we resume */ 166 } 167 packet_put_char(1); /* first kex_packet follows */ 168 packet_put_int(0); /* reserved */ 169 packet_send(); 170 171 /* Assume that resume@appgate.com will be accepted */ 172 packet_start(SSH2_MSG_KEX_ROAMING_RESUME); 173 packet_put_int(roaming_id); 174 packet_send(); 175 176 /* Read the server's kexinit and check for resume@appgate.com */ 177 if ((type = packet_read()) != SSH2_MSG_KEXINIT) { 178 debug("expected kexinit on resume, got %d", type); 179 goto fail; 180 } 181 for (i = 0; i < KEX_COOKIE_LEN; i++) 182 (void)packet_get_char(); 183 kexlist = packet_get_string(&len); 184 if (!kexlist 185 || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { 186 debug("server doesn't allow resume"); 187 goto fail; 188 } 189 xfree(str); 190 for (i = 1; i < PROPOSAL_MAX; i++) { 191 /* kex algorithm taken care of so start with i=1 and not 0 */ 192 xfree(packet_get_string(&len)); 193 } 194 i = packet_get_char(); /* first_kex_packet_follows */ 195 if (i && (c = strchr(kexlist, ','))) 196 *c = 0; 197 if (i && strcmp(kexlist, KEX_RESUME)) { 198 debug("server's kex guess (%s) was wrong, skipping", kexlist); 199 (void)packet_read(); /* Wrong guess - discard packet */ 200 } 201 202 /* 203 * Read the ROAMING_AUTH_REQUIRED challenge from the server and 204 * send ROAMING_AUTH 205 */ 206 if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { 207 debug("expected roaming_auth_required, got %d", type); 208 goto fail; 209 } 210 roaming_auth_required(); 211 212 /* Read ROAMING_AUTH_OK from the server */ 213 if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { 214 debug("expected roaming_auth_ok, got %d", type); 215 goto fail; 216 } 217 recv_bytes = packet_get_int64() ^ oldkey2; 218 debug("Peer received %llu bytes", (unsigned long long)recv_bytes); 219 resend_bytes(packet_get_connection_out(), &recv_bytes); 220 221 resume_in_progress = 0; 222 223 session_resumed = 1; /* Tell clientloop */ 224 225 return 0; 226 227fail: 228 if (kexlist) 229 xfree(kexlist); 230 if (packet_get_connection_in() == packet_get_connection_out()) 231 close(packet_get_connection_in()); 232 else { 233 close(packet_get_connection_in()); 234 close(packet_get_connection_out()); 235 } 236 return 1; 237} 238 239int 240wait_for_roaming_reconnect(void) 241{ 242 static int reenter_guard = 0; 243 int timeout_ms = options.connection_timeout * 1000; 244 int c; 245 246 if (reenter_guard != 0) 247 fatal("Server refused resume, roaming timeout may be exceeded"); 248 reenter_guard = 1; 249 250 fprintf(stderr, "[connection suspended, press return to resume]"); 251 fflush(stderr); 252 packet_backup_state(); 253 /* TODO Perhaps we should read from tty here */ 254 while ((c = fgetc(stdin)) != EOF) { 255 if (c == 'Z' - 64) { 256 kill(getpid(), SIGTSTP); 257 continue; 258 } 259 if (c != '\n' && c != '\r') 260 continue; 261 262 if (ssh_connect(host, &hostaddr, options.port, 263 options.address_family, 1, &timeout_ms, 264 options.tcp_keep_alive, options.use_privileged_port, 265 options.proxy_command) == 0 && roaming_resume() == 0) { 266 packet_restore_state(); 267 reenter_guard = 0; 268 fprintf(stderr, "[connection resumed]\n"); 269 fflush(stderr); 270 return 0; 271 } 272 273 fprintf(stderr, "[reconnect failed, press return to retry]"); 274 fflush(stderr); 275 } 276 fprintf(stderr, "[exiting]\n"); 277 fflush(stderr); 278 exit(0); 279} 280