[BACK]Return to roaming_client.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

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: }