1262566Sdes/* $OpenBSD: roaming_client.c,v 1.7 2014/01/09 23:20:00 djm Exp $ */ 2204861Sdes/* 3204861Sdes * Copyright (c) 2004-2009 AppGate Network Security AB 4204861Sdes * 5204861Sdes * Permission to use, copy, modify, and distribute this software for any 6204861Sdes * purpose with or without fee is hereby granted, provided that the above 7204861Sdes * copyright notice and this permission notice appear in all copies. 8204861Sdes * 9204861Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10204861Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11204861Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12204861Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13204861Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14204861Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15204861Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16204861Sdes */ 17204861Sdes 18204861Sdes#include "includes.h" 19204861Sdes 20204861Sdes#include "openbsd-compat/sys-queue.h" 21204861Sdes#include <sys/types.h> 22204861Sdes#include <sys/socket.h> 23204861Sdes 24204861Sdes#ifdef HAVE_INTTYPES_H 25204861Sdes#include <inttypes.h> 26204861Sdes#endif 27204861Sdes#include <signal.h> 28204861Sdes#include <string.h> 29204861Sdes#include <unistd.h> 30204861Sdes 31204861Sdes#include <openssl/crypto.h> 32204861Sdes#include <openssl/sha.h> 33204861Sdes 34204861Sdes#include "xmalloc.h" 35204861Sdes#include "buffer.h" 36204861Sdes#include "channels.h" 37204861Sdes#include "cipher.h" 38204861Sdes#include "dispatch.h" 39204861Sdes#include "clientloop.h" 40204861Sdes#include "log.h" 41204861Sdes#include "match.h" 42204861Sdes#include "misc.h" 43204861Sdes#include "packet.h" 44204861Sdes#include "ssh.h" 45204861Sdes#include "key.h" 46204861Sdes#include "kex.h" 47204861Sdes#include "readconf.h" 48204861Sdes#include "roaming.h" 49204861Sdes#include "ssh2.h" 50204861Sdes#include "sshconnect.h" 51262566Sdes#include "digest.h" 52204861Sdes 53204861Sdes/* import */ 54204861Sdesextern Options options; 55204861Sdesextern char *host; 56204861Sdesextern struct sockaddr_storage hostaddr; 57204861Sdesextern int session_resumed; 58204861Sdes 59204861Sdesstatic u_int32_t roaming_id; 60204861Sdesstatic u_int64_t cookie; 61204861Sdesstatic u_int64_t lastseenchall; 62204861Sdesstatic u_int64_t key1, key2, oldkey1, oldkey2; 63204861Sdes 64204861Sdesvoid 65204861Sdesroaming_reply(int type, u_int32_t seq, void *ctxt) 66204861Sdes{ 67204861Sdes if (type == SSH2_MSG_REQUEST_FAILURE) { 68204861Sdes logit("Server denied roaming"); 69204861Sdes return; 70204861Sdes } 71204861Sdes verbose("Roaming enabled"); 72204861Sdes roaming_id = packet_get_int(); 73204861Sdes cookie = packet_get_int64(); 74204861Sdes key1 = oldkey1 = packet_get_int64(); 75204861Sdes key2 = oldkey2 = packet_get_int64(); 76240075Sdes set_out_buffer_size(packet_get_int() + get_snd_buf_size()); 77204861Sdes roaming_enabled = 1; 78204861Sdes} 79204861Sdes 80204861Sdesvoid 81204861Sdesrequest_roaming(void) 82204861Sdes{ 83204861Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 84204861Sdes packet_put_cstring(ROAMING_REQUEST); 85204861Sdes packet_put_char(1); 86204861Sdes packet_put_int(get_recv_buf_size()); 87204861Sdes packet_send(); 88204861Sdes client_register_global_confirm(roaming_reply, NULL); 89204861Sdes} 90204861Sdes 91204861Sdesstatic void 92204861Sdesroaming_auth_required(void) 93204861Sdes{ 94262566Sdes u_char digest[SSH_DIGEST_MAX_LENGTH]; 95204861Sdes Buffer b; 96204861Sdes u_int64_t chall, oldchall; 97204861Sdes 98204861Sdes chall = packet_get_int64(); 99204861Sdes oldchall = packet_get_int64(); 100204861Sdes if (oldchall != lastseenchall) { 101204861Sdes key1 = oldkey1; 102204861Sdes key2 = oldkey2; 103204861Sdes } 104204861Sdes lastseenchall = chall; 105204861Sdes 106204861Sdes buffer_init(&b); 107204861Sdes buffer_put_int64(&b, cookie); 108204861Sdes buffer_put_int64(&b, chall); 109262566Sdes if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, digest, sizeof(digest)) != 0) 110262566Sdes fatal("%s: ssh_digest_buffer failed", __func__); 111204861Sdes buffer_free(&b); 112204861Sdes 113204861Sdes packet_start(SSH2_MSG_KEX_ROAMING_AUTH); 114204861Sdes packet_put_int64(key1 ^ get_recv_bytes()); 115262566Sdes packet_put_raw(digest, ssh_digest_bytes(SSH_DIGEST_SHA1)); 116204861Sdes packet_send(); 117204861Sdes 118204861Sdes oldkey1 = key1; 119204861Sdes oldkey2 = key2; 120204861Sdes calculate_new_key(&key1, cookie, chall); 121204861Sdes calculate_new_key(&key2, cookie, chall); 122204861Sdes 123204861Sdes debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); 124204861Sdes debug("Sent roaming_auth packet"); 125204861Sdes} 126204861Sdes 127204861Sdesint 128204861Sdesresume_kex(void) 129204861Sdes{ 130204861Sdes /* 131204861Sdes * This should not happen - if the client sends the kex method 132204861Sdes * resume@appgate.com then the kex is done in roaming_resume(). 133204861Sdes */ 134204861Sdes return 1; 135204861Sdes} 136204861Sdes 137204861Sdesstatic int 138204861Sdesroaming_resume(void) 139204861Sdes{ 140204861Sdes u_int64_t recv_bytes; 141204861Sdes char *str = NULL, *kexlist = NULL, *c; 142204861Sdes int i, type; 143204861Sdes int timeout_ms = options.connection_timeout * 1000; 144204861Sdes u_int len; 145204861Sdes u_int32_t rnd = 0; 146204861Sdes 147204861Sdes resume_in_progress = 1; 148204861Sdes 149204861Sdes /* Exchange banners */ 150204861Sdes ssh_exchange_identification(timeout_ms); 151204861Sdes packet_set_nonblocking(); 152204861Sdes 153204861Sdes /* Send a kexinit message with resume@appgate.com as only kex algo */ 154204861Sdes packet_start(SSH2_MSG_KEXINIT); 155204861Sdes for (i = 0; i < KEX_COOKIE_LEN; i++) { 156204861Sdes if (i % 4 == 0) 157204861Sdes rnd = arc4random(); 158204861Sdes packet_put_char(rnd & 0xff); 159204861Sdes rnd >>= 8; 160204861Sdes } 161204861Sdes packet_put_cstring(KEX_RESUME); 162204861Sdes for (i = 1; i < PROPOSAL_MAX; i++) { 163204861Sdes /* kex algorithm added so start with i=1 and not 0 */ 164204861Sdes packet_put_cstring(""); /* Not used when we resume */ 165204861Sdes } 166204861Sdes packet_put_char(1); /* first kex_packet follows */ 167204861Sdes packet_put_int(0); /* reserved */ 168204861Sdes packet_send(); 169204861Sdes 170204861Sdes /* Assume that resume@appgate.com will be accepted */ 171204861Sdes packet_start(SSH2_MSG_KEX_ROAMING_RESUME); 172204861Sdes packet_put_int(roaming_id); 173204861Sdes packet_send(); 174204861Sdes 175204861Sdes /* Read the server's kexinit and check for resume@appgate.com */ 176204861Sdes if ((type = packet_read()) != SSH2_MSG_KEXINIT) { 177204861Sdes debug("expected kexinit on resume, got %d", type); 178204861Sdes goto fail; 179204861Sdes } 180204861Sdes for (i = 0; i < KEX_COOKIE_LEN; i++) 181204861Sdes (void)packet_get_char(); 182204861Sdes kexlist = packet_get_string(&len); 183204861Sdes if (!kexlist 184204861Sdes || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { 185204861Sdes debug("server doesn't allow resume"); 186204861Sdes goto fail; 187204861Sdes } 188255767Sdes free(str); 189204861Sdes for (i = 1; i < PROPOSAL_MAX; i++) { 190204861Sdes /* kex algorithm taken care of so start with i=1 and not 0 */ 191255767Sdes free(packet_get_string(&len)); 192204861Sdes } 193204861Sdes i = packet_get_char(); /* first_kex_packet_follows */ 194204861Sdes if (i && (c = strchr(kexlist, ','))) 195204861Sdes *c = 0; 196204861Sdes if (i && strcmp(kexlist, KEX_RESUME)) { 197204861Sdes debug("server's kex guess (%s) was wrong, skipping", kexlist); 198204861Sdes (void)packet_read(); /* Wrong guess - discard packet */ 199204861Sdes } 200204861Sdes 201204861Sdes /* 202204861Sdes * Read the ROAMING_AUTH_REQUIRED challenge from the server and 203204861Sdes * send ROAMING_AUTH 204204861Sdes */ 205204861Sdes if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { 206204861Sdes debug("expected roaming_auth_required, got %d", type); 207204861Sdes goto fail; 208204861Sdes } 209204861Sdes roaming_auth_required(); 210204861Sdes 211204861Sdes /* Read ROAMING_AUTH_OK from the server */ 212204861Sdes if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { 213204861Sdes debug("expected roaming_auth_ok, got %d", type); 214204861Sdes goto fail; 215204861Sdes } 216204861Sdes recv_bytes = packet_get_int64() ^ oldkey2; 217204861Sdes debug("Peer received %llu bytes", (unsigned long long)recv_bytes); 218204861Sdes resend_bytes(packet_get_connection_out(), &recv_bytes); 219204861Sdes 220204861Sdes resume_in_progress = 0; 221204861Sdes 222204861Sdes session_resumed = 1; /* Tell clientloop */ 223204861Sdes 224204861Sdes return 0; 225204861Sdes 226204861Sdesfail: 227255767Sdes free(kexlist); 228204861Sdes if (packet_get_connection_in() == packet_get_connection_out()) 229204861Sdes close(packet_get_connection_in()); 230204861Sdes else { 231204861Sdes close(packet_get_connection_in()); 232204861Sdes close(packet_get_connection_out()); 233204861Sdes } 234204861Sdes return 1; 235204861Sdes} 236204861Sdes 237204861Sdesint 238204861Sdeswait_for_roaming_reconnect(void) 239204861Sdes{ 240204861Sdes static int reenter_guard = 0; 241204861Sdes int timeout_ms = options.connection_timeout * 1000; 242204861Sdes int c; 243204861Sdes 244204861Sdes if (reenter_guard != 0) 245204861Sdes fatal("Server refused resume, roaming timeout may be exceeded"); 246204861Sdes reenter_guard = 1; 247204861Sdes 248204861Sdes fprintf(stderr, "[connection suspended, press return to resume]"); 249204861Sdes fflush(stderr); 250204861Sdes packet_backup_state(); 251204861Sdes /* TODO Perhaps we should read from tty here */ 252204861Sdes while ((c = fgetc(stdin)) != EOF) { 253204861Sdes if (c == 'Z' - 64) { 254204861Sdes kill(getpid(), SIGTSTP); 255204861Sdes continue; 256204861Sdes } 257204861Sdes if (c != '\n' && c != '\r') 258204861Sdes continue; 259204861Sdes 260262566Sdes if (ssh_connect(host, NULL, &hostaddr, options.port, 261204861Sdes options.address_family, 1, &timeout_ms, 262262566Sdes options.tcp_keep_alive, options.use_privileged_port) == 0 && 263262566Sdes roaming_resume() == 0) { 264204861Sdes packet_restore_state(); 265204861Sdes reenter_guard = 0; 266204861Sdes fprintf(stderr, "[connection resumed]\n"); 267204861Sdes fflush(stderr); 268204861Sdes return 0; 269204861Sdes } 270204861Sdes 271204861Sdes fprintf(stderr, "[reconnect failed, press return to retry]"); 272204861Sdes fflush(stderr); 273204861Sdes } 274204861Sdes fprintf(stderr, "[exiting]\n"); 275204861Sdes fflush(stderr); 276204861Sdes exit(0); 277204861Sdes} 278