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