roaming_client.c revision 240075
1240075Sdes/* $OpenBSD: roaming_client.c,v 1.4 2011/12/07 05:44:38 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" 51204861Sdes 52204861Sdes/* import */ 53204861Sdesextern Options options; 54204861Sdesextern char *host; 55204861Sdesextern struct sockaddr_storage hostaddr; 56204861Sdesextern int session_resumed; 57204861Sdes 58204861Sdesstatic u_int32_t roaming_id; 59204861Sdesstatic u_int64_t cookie; 60204861Sdesstatic u_int64_t lastseenchall; 61204861Sdesstatic u_int64_t key1, key2, oldkey1, oldkey2; 62204861Sdes 63204861Sdesvoid 64204861Sdesroaming_reply(int type, u_int32_t seq, void *ctxt) 65204861Sdes{ 66204861Sdes if (type == SSH2_MSG_REQUEST_FAILURE) { 67204861Sdes logit("Server denied roaming"); 68204861Sdes return; 69204861Sdes } 70204861Sdes verbose("Roaming enabled"); 71204861Sdes roaming_id = packet_get_int(); 72204861Sdes cookie = packet_get_int64(); 73204861Sdes key1 = oldkey1 = packet_get_int64(); 74204861Sdes key2 = oldkey2 = packet_get_int64(); 75240075Sdes set_out_buffer_size(packet_get_int() + get_snd_buf_size()); 76204861Sdes roaming_enabled = 1; 77204861Sdes} 78204861Sdes 79204861Sdesvoid 80204861Sdesrequest_roaming(void) 81204861Sdes{ 82204861Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 83204861Sdes packet_put_cstring(ROAMING_REQUEST); 84204861Sdes packet_put_char(1); 85204861Sdes packet_put_int(get_recv_buf_size()); 86204861Sdes packet_send(); 87204861Sdes client_register_global_confirm(roaming_reply, NULL); 88204861Sdes} 89204861Sdes 90204861Sdesstatic void 91204861Sdesroaming_auth_required(void) 92204861Sdes{ 93204861Sdes u_char digest[SHA_DIGEST_LENGTH]; 94204861Sdes EVP_MD_CTX md; 95204861Sdes Buffer b; 96204861Sdes const EVP_MD *evp_md = EVP_sha1(); 97204861Sdes u_int64_t chall, oldchall; 98204861Sdes 99204861Sdes chall = packet_get_int64(); 100204861Sdes oldchall = packet_get_int64(); 101204861Sdes if (oldchall != lastseenchall) { 102204861Sdes key1 = oldkey1; 103204861Sdes key2 = oldkey2; 104204861Sdes } 105204861Sdes lastseenchall = chall; 106204861Sdes 107204861Sdes buffer_init(&b); 108204861Sdes buffer_put_int64(&b, cookie); 109204861Sdes buffer_put_int64(&b, chall); 110204861Sdes EVP_DigestInit(&md, evp_md); 111204861Sdes EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 112204861Sdes EVP_DigestFinal(&md, digest, NULL); 113204861Sdes buffer_free(&b); 114204861Sdes 115204861Sdes packet_start(SSH2_MSG_KEX_ROAMING_AUTH); 116204861Sdes packet_put_int64(key1 ^ get_recv_bytes()); 117204861Sdes packet_put_raw(digest, sizeof(digest)); 118204861Sdes packet_send(); 119204861Sdes 120204861Sdes oldkey1 = key1; 121204861Sdes oldkey2 = key2; 122204861Sdes calculate_new_key(&key1, cookie, chall); 123204861Sdes calculate_new_key(&key2, cookie, chall); 124204861Sdes 125204861Sdes debug("Received %llu bytes", (unsigned long long)get_recv_bytes()); 126204861Sdes debug("Sent roaming_auth packet"); 127204861Sdes} 128204861Sdes 129204861Sdesint 130204861Sdesresume_kex(void) 131204861Sdes{ 132204861Sdes /* 133204861Sdes * This should not happen - if the client sends the kex method 134204861Sdes * resume@appgate.com then the kex is done in roaming_resume(). 135204861Sdes */ 136204861Sdes return 1; 137204861Sdes} 138204861Sdes 139204861Sdesstatic int 140204861Sdesroaming_resume(void) 141204861Sdes{ 142204861Sdes u_int64_t recv_bytes; 143204861Sdes char *str = NULL, *kexlist = NULL, *c; 144204861Sdes int i, type; 145204861Sdes int timeout_ms = options.connection_timeout * 1000; 146204861Sdes u_int len; 147204861Sdes u_int32_t rnd = 0; 148204861Sdes 149204861Sdes resume_in_progress = 1; 150204861Sdes 151204861Sdes /* Exchange banners */ 152204861Sdes ssh_exchange_identification(timeout_ms); 153204861Sdes packet_set_nonblocking(); 154204861Sdes 155204861Sdes /* Send a kexinit message with resume@appgate.com as only kex algo */ 156204861Sdes packet_start(SSH2_MSG_KEXINIT); 157204861Sdes for (i = 0; i < KEX_COOKIE_LEN; i++) { 158204861Sdes if (i % 4 == 0) 159204861Sdes rnd = arc4random(); 160204861Sdes packet_put_char(rnd & 0xff); 161204861Sdes rnd >>= 8; 162204861Sdes } 163204861Sdes packet_put_cstring(KEX_RESUME); 164204861Sdes for (i = 1; i < PROPOSAL_MAX; i++) { 165204861Sdes /* kex algorithm added so start with i=1 and not 0 */ 166204861Sdes packet_put_cstring(""); /* Not used when we resume */ 167204861Sdes } 168204861Sdes packet_put_char(1); /* first kex_packet follows */ 169204861Sdes packet_put_int(0); /* reserved */ 170204861Sdes packet_send(); 171204861Sdes 172204861Sdes /* Assume that resume@appgate.com will be accepted */ 173204861Sdes packet_start(SSH2_MSG_KEX_ROAMING_RESUME); 174204861Sdes packet_put_int(roaming_id); 175204861Sdes packet_send(); 176204861Sdes 177204861Sdes /* Read the server's kexinit and check for resume@appgate.com */ 178204861Sdes if ((type = packet_read()) != SSH2_MSG_KEXINIT) { 179204861Sdes debug("expected kexinit on resume, got %d", type); 180204861Sdes goto fail; 181204861Sdes } 182204861Sdes for (i = 0; i < KEX_COOKIE_LEN; i++) 183204861Sdes (void)packet_get_char(); 184204861Sdes kexlist = packet_get_string(&len); 185204861Sdes if (!kexlist 186204861Sdes || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) { 187204861Sdes debug("server doesn't allow resume"); 188204861Sdes goto fail; 189204861Sdes } 190204861Sdes xfree(str); 191204861Sdes for (i = 1; i < PROPOSAL_MAX; i++) { 192204861Sdes /* kex algorithm taken care of so start with i=1 and not 0 */ 193204861Sdes xfree(packet_get_string(&len)); 194204861Sdes } 195204861Sdes i = packet_get_char(); /* first_kex_packet_follows */ 196204861Sdes if (i && (c = strchr(kexlist, ','))) 197204861Sdes *c = 0; 198204861Sdes if (i && strcmp(kexlist, KEX_RESUME)) { 199204861Sdes debug("server's kex guess (%s) was wrong, skipping", kexlist); 200204861Sdes (void)packet_read(); /* Wrong guess - discard packet */ 201204861Sdes } 202204861Sdes 203204861Sdes /* 204204861Sdes * Read the ROAMING_AUTH_REQUIRED challenge from the server and 205204861Sdes * send ROAMING_AUTH 206204861Sdes */ 207204861Sdes if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) { 208204861Sdes debug("expected roaming_auth_required, got %d", type); 209204861Sdes goto fail; 210204861Sdes } 211204861Sdes roaming_auth_required(); 212204861Sdes 213204861Sdes /* Read ROAMING_AUTH_OK from the server */ 214204861Sdes if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) { 215204861Sdes debug("expected roaming_auth_ok, got %d", type); 216204861Sdes goto fail; 217204861Sdes } 218204861Sdes recv_bytes = packet_get_int64() ^ oldkey2; 219204861Sdes debug("Peer received %llu bytes", (unsigned long long)recv_bytes); 220204861Sdes resend_bytes(packet_get_connection_out(), &recv_bytes); 221204861Sdes 222204861Sdes resume_in_progress = 0; 223204861Sdes 224204861Sdes session_resumed = 1; /* Tell clientloop */ 225204861Sdes 226204861Sdes return 0; 227204861Sdes 228204861Sdesfail: 229204861Sdes if (kexlist) 230204861Sdes xfree(kexlist); 231204861Sdes if (packet_get_connection_in() == packet_get_connection_out()) 232204861Sdes close(packet_get_connection_in()); 233204861Sdes else { 234204861Sdes close(packet_get_connection_in()); 235204861Sdes close(packet_get_connection_out()); 236204861Sdes } 237204861Sdes return 1; 238204861Sdes} 239204861Sdes 240204861Sdesint 241204861Sdeswait_for_roaming_reconnect(void) 242204861Sdes{ 243204861Sdes static int reenter_guard = 0; 244204861Sdes int timeout_ms = options.connection_timeout * 1000; 245204861Sdes int c; 246204861Sdes 247204861Sdes if (reenter_guard != 0) 248204861Sdes fatal("Server refused resume, roaming timeout may be exceeded"); 249204861Sdes reenter_guard = 1; 250204861Sdes 251204861Sdes fprintf(stderr, "[connection suspended, press return to resume]"); 252204861Sdes fflush(stderr); 253204861Sdes packet_backup_state(); 254204861Sdes /* TODO Perhaps we should read from tty here */ 255204861Sdes while ((c = fgetc(stdin)) != EOF) { 256204861Sdes if (c == 'Z' - 64) { 257204861Sdes kill(getpid(), SIGTSTP); 258204861Sdes continue; 259204861Sdes } 260204861Sdes if (c != '\n' && c != '\r') 261204861Sdes continue; 262204861Sdes 263204861Sdes if (ssh_connect(host, &hostaddr, options.port, 264204861Sdes options.address_family, 1, &timeout_ms, 265204861Sdes options.tcp_keep_alive, options.use_privileged_port, 266204861Sdes options.proxy_command) == 0 && roaming_resume() == 0) { 267204861Sdes packet_restore_state(); 268204861Sdes reenter_guard = 0; 269204861Sdes fprintf(stderr, "[connection resumed]\n"); 270204861Sdes fflush(stderr); 271204861Sdes return 0; 272204861Sdes } 273204861Sdes 274204861Sdes fprintf(stderr, "[reconnect failed, press return to retry]"); 275204861Sdes fflush(stderr); 276204861Sdes } 277204861Sdes fprintf(stderr, "[exiting]\n"); 278204861Sdes fflush(stderr); 279204861Sdes exit(0); 280204861Sdes} 281