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