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