1187706Sgonzo/* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */ 2187706Sgonzo/* 3187706Sgonzo * Copyright (c) 2004-2009 AppGate Network Security AB 4187706Sgonzo * 5187706Sgonzo * Permission to use, copy, modify, and distribute this software for any 6187706Sgonzo * purpose with or without fee is hereby granted, provided that the above 7187706Sgonzo * copyright notice and this permission notice appear in all copies. 8187706Sgonzo * 9187706Sgonzo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10187706Sgonzo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11187706Sgonzo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12187706Sgonzo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13187706Sgonzo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14187706Sgonzo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15187706Sgonzo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16187706Sgonzo */ 17187706Sgonzo 18187706Sgonzo#include "includes.h" 19187706Sgonzo 20187706Sgonzo#include "openbsd-compat/sys-queue.h" 21187706Sgonzo#include <sys/types.h> 22187706Sgonzo#include <sys/socket.h> 23187706Sgonzo 24187706Sgonzo#ifdef HAVE_INTTYPES_H 25187706Sgonzo#include <inttypes.h> 26187706Sgonzo#endif 27187706Sgonzo#include <signal.h> 28187706Sgonzo#include <string.h> 29187706Sgonzo#include <unistd.h> 30187706Sgonzo 31230195Sadrian#include <openssl/crypto.h> 32230195Sadrian#include <openssl/sha.h> 33187706Sgonzo 34187706Sgonzo#include "xmalloc.h" 35187706Sgonzo#include "buffer.h" 36187706Sgonzo#include "channels.h" 37187706Sgonzo#include "cipher.h" 38187706Sgonzo#include "dispatch.h" 39187706Sgonzo#include "clientloop.h" 40187706Sgonzo#include "log.h" 41187706Sgonzo#include "match.h" 42234365Sadrian#include "misc.h" 43234365Sadrian#include "packet.h" 44187706Sgonzo#include "ssh.h" 45187706Sgonzo#include "key.h" 46187706Sgonzo#include "kex.h" 47187706Sgonzo#include "readconf.h" 48187706Sgonzo#include "roaming.h" 49187706Sgonzo#include "ssh2.h" 50187706Sgonzo#include "sshconnect.h" 51210900Sgonzo 52187706Sgonzo/* import */ 53187706Sgonzoextern Options options; 54187706Sgonzoextern char *host; 55187706Sgonzoextern struct sockaddr_storage hostaddr; 56187706Sgonzoextern int session_resumed; 57187706Sgonzo 58187706Sgonzostatic u_int32_t roaming_id; 59192161Sgonzostatic u_int64_t cookie; 60192161Sgonzostatic u_int64_t lastseenchall; 61187706Sgonzostatic u_int64_t key1, key2, oldkey1, oldkey2; 62211478Sadrian 63211478Sadrianvoid 64234217Sadrianroaming_reply(int type, u_int32_t seq, void *ctxt) 65234485Sadrian{ 66234217Sadrian if (type == SSH2_MSG_REQUEST_FAILURE) { 67234217Sadrian logit("Server denied roaming"); 68234366Sadrian return; 69234366Sadrian } 70234366Sadrian verbose("Roaming enabled"); 71187706Sgonzo roaming_id = packet_get_int(); 72234366Sadrian cookie = packet_get_int64(); 73187706Sgonzo key1 = oldkey1 = packet_get_int64(); 74187706Sgonzo key2 = oldkey2 = packet_get_int64(); 75234365Sadrian set_out_buffer_size(packet_get_int() + get_snd_buf_size()); 76234365Sadrian roaming_enabled = 1; 77234365Sadrian} 78234365Sadrian 79187706Sgonzovoid 80187706Sgonzorequest_roaming(void) 81187706Sgonzo{ 82187706Sgonzo packet_start(SSH2_MSG_GLOBAL_REQUEST); 83245112Smonthadar packet_put_cstring(ROAMING_REQUEST); 84187706Sgonzo packet_put_char(1); 85187706Sgonzo packet_put_int(get_recv_buf_size()); 86187706Sgonzo packet_send(); 87191872Sgonzo client_register_global_confirm(roaming_reply, NULL); 88210900Sgonzo} 89187706Sgonzo 90187706Sgonzostatic void 91187706Sgonzoroaming_auth_required(void) 92187706Sgonzo{ 93191872Sgonzo u_char digest[SHA_DIGEST_LENGTH]; 94191872Sgonzo EVP_MD_CTX md; 95191872Sgonzo Buffer b; 96191872Sgonzo const EVP_MD *evp_md = EVP_sha1(); 97191872Sgonzo u_int64_t chall, oldchall; 98191872Sgonzo 99234366Sadrian chall = packet_get_int64(); 100192822Sgonzo oldchall = packet_get_int64(); 101191872Sgonzo if (oldchall != lastseenchall) { 102191872Sgonzo key1 = oldkey1; 103192822Sgonzo key2 = oldkey2; 104191872Sgonzo } 105234365Sadrian lastseenchall = chall; 106191872Sgonzo 107194273Sgonzo buffer_init(&b); 108194273Sgonzo buffer_put_int64(&b, cookie); 109191872Sgonzo buffer_put_int64(&b, chall); 110191872Sgonzo EVP_DigestInit(&md, evp_md); 111191872Sgonzo EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 112234366Sadrian EVP_DigestFinal(&md, digest, NULL); 113192822Sgonzo buffer_free(&b); 114191872Sgonzo 115191872Sgonzo packet_start(SSH2_MSG_KEX_ROAMING_AUTH); 116192822Sgonzo packet_put_int64(key1 ^ get_recv_bytes()); 117191872Sgonzo packet_put_raw(digest, sizeof(digest)); 118234365Sadrian packet_send(); 119191872Sgonzo 120191872Sgonzo oldkey1 = key1; 121194273Sgonzo oldkey2 = key2; 122194273Sgonzo calculate_new_key(&key1, cookie, chall); 123191872Sgonzo calculate_new_key(&key2, cookie, chall); 124191872Sgonzo 125234366Sadrian debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); 126234366Sadrian debug("Sent roaming_auth packet"); 127234366Sadrian} 128187706Sgonzo 129187706Sgonzoint 130187706Sgonzoresume_kex(void) 131187706Sgonzo{ 132187706Sgonzo /* 133187706Sgonzo * This should not happen - if the client sends the kex method 134234366Sadrian * resume@appgate.com then the kex is done in roaming_resume(). 135187706Sgonzo */ 136187706Sgonzo return 1; 137187706Sgonzo} 138187706Sgonzo 139187706Sgonzostatic int 140187706Sgonzoroaming_resume(void) 141187706Sgonzo{ 142187706Sgonzo u_int64_t recv_bytes; 143187706Sgonzo char *str = NULL, *kexlist = NULL, *c; 144187706Sgonzo int i, type; 145187706Sgonzo int timeout_ms = options.connection_timeout * 1000; 146187706Sgonzo u_int len; 147234366Sadrian u_int32_t rnd = 0; 148187706Sgonzo 149187706Sgonzo resume_in_progress = 1; 150187706Sgonzo 151234365Sadrian /* Exchange banners */ 152234365Sadrian ssh_exchange_identification(timeout_ms); 153234365Sadrian packet_set_nonblocking(); 154187706Sgonzo 155187706Sgonzo /* Send a kexinit message with resume@appgate.com as only kex algo */ 156187706Sgonzo packet_start(SSH2_MSG_KEXINIT); 157187706Sgonzo for (i = 0; i < KEX_COOKIE_LEN; i++) { 158187706Sgonzo if (i % 4 == 0) 159187706Sgonzo rnd = arc4random(); 160187706Sgonzo packet_put_char(rnd & 0xff); 161187706Sgonzo rnd >>= 8; 162187706Sgonzo } 163187706Sgonzo packet_put_cstring(KEX_RESUME); 164187706Sgonzo for (i = 1; i < PROPOSAL_MAX; i++) { 165187706Sgonzo /* kex algorithm added so start with i=1 and not 0 */ 166187706Sgonzo packet_put_cstring(""); /* Not used when we resume */ 167187706Sgonzo } 168187706Sgonzo packet_put_char(1); /* first kex_packet follows */ 169187706Sgonzo packet_put_int(0); /* reserved */ 170187706Sgonzo packet_send(); 171187706Sgonzo 172187706Sgonzo /* Assume that resume@appgate.com will be accepted */ 173187706Sgonzo packet_start(SSH2_MSG_KEX_ROAMING_RESUME); 174187706Sgonzo packet_put_int(roaming_id); 175187706Sgonzo packet_send(); 176187706Sgonzo 177187706Sgonzo /* Read the server's kexinit and check for resume@appgate.com */ 178187706Sgonzo if ((type = packet_read()) != SSH2_MSG_KEXINIT) { 179187706Sgonzo debug("expected kexinit on resume, got %d", type); 180187706Sgonzo goto fail; 181187706Sgonzo } 182187706Sgonzo for (i = 0; i < KEX_COOKIE_LEN; i++) 183187706Sgonzo (void)packet_get_char(); 184187706Sgonzo kexlist = packet_get_string(&len); 185187706Sgonzo if (!kexlist 186187706Sgonzo || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { 187187706Sgonzo debug("server doesn't allow resume"); 188234366Sadrian goto fail; 189187706Sgonzo } 190187706Sgonzo free(str); 191187706Sgonzo for (i = 1; i < PROPOSAL_MAX; i++) { 192187706Sgonzo /* kex algorithm taken care of so start with i=1 and not 0 */ 193187706Sgonzo free(packet_get_string(&len)); 194234366Sadrian } 195187706Sgonzo i = packet_get_char(); /* first_kex_packet_follows */ 196187706Sgonzo if (i && (c = strchr(kexlist, ','))) 197187706Sgonzo *c = 0; 198234365Sadrian if (i && strcmp(kexlist, KEX_RESUME)) { 199234365Sadrian debug("server's kex guess (%s) was wrong, skipping", kexlist); 200234365Sadrian (void)packet_read(); /* Wrong guess - discard packet */ 201234366Sadrian } 202187706Sgonzo 203187706Sgonzo /* 204187706Sgonzo * Read the ROAMING_AUTH_REQUIRED challenge from the server and 205187706Sgonzo * send ROAMING_AUTH 206187706Sgonzo */ 207187706Sgonzo if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { 208187706Sgonzo debug("expected roaming_auth_required, got %d", type); 209187706Sgonzo goto fail; 210187706Sgonzo } 211187706Sgonzo roaming_auth_required(); 212234366Sadrian 213194059Sgonzo /* Read ROAMING_AUTH_OK from the server */ 214187706Sgonzo if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { 215187706Sgonzo debug("expected roaming_auth_ok, got %d", type); 216234205Sadrian goto fail; 217187706Sgonzo } 218187706Sgonzo recv_bytes = packet_get_int64() ^ oldkey2; 219187706Sgonzo debug("Peer received %llu bytes", (unsigned long long)recv_bytes); 220234306Sadrian resend_bytes(packet_get_connection_out(), &recv_bytes); 221234306Sadrian 222234306Sadrian resume_in_progress = 0; 223234306Sadrian 224234306Sadrian session_resumed = 1; /* Tell clientloop */ 225234306Sadrian 226187706Sgonzo return 0; 227187706Sgonzo 228187706Sgonzofail: 229187706Sgonzo free(kexlist); 230187706Sgonzo if (packet_get_connection_in() == packet_get_connection_out()) 231187706Sgonzo close(packet_get_connection_in()); 232234365Sadrian else { 233234204Sadrian close(packet_get_connection_in()); 234234204Sadrian close(packet_get_connection_out()); 235234204Sadrian } 236234204Sadrian return 1; 237234204Sadrian} 238234365Sadrian 239187706Sgonzoint 240187706Sgonzowait_for_roaming_reconnect(void) 241187706Sgonzo{ 242187706Sgonzo static int reenter_guard = 0; 243234366Sadrian int timeout_ms = options.connection_timeout * 1000; 244187706Sgonzo int c; 245187706Sgonzo 246187706Sgonzo if (reenter_guard != 0) 247187706Sgonzo fatal("Server refused resume, roaming timeout may be exceeded"); 248187706Sgonzo reenter_guard = 1; 249234204Sadrian 250187706Sgonzo fprintf(stderr, "[connection suspended, press return to resume]"); 251187706Sgonzo fflush(stderr); 252187706Sgonzo packet_backup_state(); 253234204Sadrian /* TODO Perhaps we should read from tty here */ 254234204Sadrian while ((c = fgetc(stdin)) != EOF) { 255234204Sadrian if (c == 'Z' - 64) { 256234204Sadrian kill(getpid(), SIGTSTP); 257234204Sadrian continue; 258234365Sadrian } 259234204Sadrian if (c != '\n' && c != '\r') 260234204Sadrian continue; 261234365Sadrian 262234204Sadrian if (ssh_connect(host, &hostaddr, options.port, 263234204Sadrian options.address_family, 1, &timeout_ms, 264234204Sadrian options.tcp_keep_alive, options.use_privileged_port, 265234204Sadrian options.proxy_command) == 0 && roaming_resume() == 0) { 266234204Sadrian packet_restore_state(); 267234204Sadrian reenter_guard = 0; 268234204Sadrian fprintf(stderr, "[connection resumed]\n"); 269234204Sadrian fflush(stderr); 270187706Sgonzo return 0; 271187706Sgonzo } 272187706Sgonzo 273234365Sadrian fprintf(stderr, "[reconnect failed, press return to retry]"); 274234204Sadrian fflush(stderr); 275234204Sadrian } 276234204Sadrian fprintf(stderr, "[exiting]\n"); 277234365Sadrian fflush(stderr); 278187706Sgonzo exit(0); 279187706Sgonzo} 280230148Sadrian