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

Annotation of src/usr.bin/ssh/channel-x11.c, Revision 1.1

1.1     ! markus      1: /*
        !             2:  * Author: Tatu Ylonen <ylo@cs.hut.fi>
        !             3:  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
        !             4:  *                    All rights reserved
        !             5:  *
        !             6:  * As far as I am concerned, the code I have written for this software
        !             7:  * can be used freely for any purpose.  Any derived versions of this
        !             8:  * software must be clearly marked as such, and if the derived work is
        !             9:  * incompatible with the protocol description in the RFC file, it must be
        !            10:  * called by a name other than "ssh" or "Secure Shell".
        !            11:  *
        !            12:  *
        !            13:  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
        !            14:  *
        !            15:  * Redistribution and use in source and binary forms, with or without
        !            16:  * modification, are permitted provided that the following conditions
        !            17:  * are met:
        !            18:  * 1. Redistributions of source code must retain the above copyright
        !            19:  *    notice, this list of conditions and the following disclaimer.
        !            20:  * 2. Redistributions in binary form must reproduce the above copyright
        !            21:  *    notice, this list of conditions and the following disclaimer in the
        !            22:  *    documentation and/or other materials provided with the distribution.
        !            23:  *
        !            24:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            25:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
        !            26:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
        !            27:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
        !            28:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
        !            29:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
        !            30:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
        !            31:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            32:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
        !            33:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            34:  */
        !            35:
        !            36: #include "includes.h"
        !            37: RCSID("$OpenBSD: channels.c,v 1.119 2001/05/28 23:25:24 markus Exp $");
        !            38:
        !            39: #include "ssh1.h"
        !            40: #include "packet.h"
        !            41: #include "xmalloc.h"
        !            42: #include "log.h"
        !            43: #include "compat.h"
        !            44: #include "channel.h"
        !            45:
        !            46: /* Maximum number of fake X11 displays to try. */
        !            47: #define MAX_DISPLAYS  1000
        !            48:
        !            49: /* Saved X11 authentication protocol name. */
        !            50: char *x11_saved_proto = NULL;
        !            51:
        !            52: /* Saved X11 authentication data.  This is the real data. */
        !            53: char *x11_saved_data = NULL;
        !            54: u_int x11_saved_data_len = 0;
        !            55:
        !            56: /*
        !            57:  * Fake X11 authentication data.  This is what the server will be sending us;
        !            58:  * we should replace any occurrences of this by the real data.
        !            59:  */
        !            60: char *x11_fake_data = NULL;
        !            61: u_int x11_fake_data_len;
        !            62:
        !            63: extern int IPv4or6;
        !            64:
        !            65: #define        NUM_SOCKS       10
        !            66:
        !            67: /*
        !            68:  * This is a special state for X11 authentication spoofing.  An opened X11
        !            69:  * connection (when authentication spoofing is being done) remains in this
        !            70:  * state until the first packet has been completely read.  The authentication
        !            71:  * data in that packet is then substituted by the real data if it matches the
        !            72:  * fake data, and the channel is put into normal mode.
        !            73:  * XXX All this happens at the client side.
        !            74:  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
        !            75:  */
        !            76: int
        !            77: x11_check_cookie(Buffer *b)
        !            78: {
        !            79:        u_char *ucp;
        !            80:        u_int proto_len, data_len;
        !            81:
        !            82:        /* Check if the fixed size part of the packet is in buffer. */
        !            83:        if (buffer_len(b) < 12)
        !            84:                return 0;
        !            85:
        !            86:        /* Parse the lengths of variable-length fields. */
        !            87:        ucp = (u_char *) buffer_ptr(b);
        !            88:        if (ucp[0] == 0x42) {   /* Byte order MSB first. */
        !            89:                proto_len = 256 * ucp[6] + ucp[7];
        !            90:                data_len = 256 * ucp[8] + ucp[9];
        !            91:        } else if (ucp[0] == 0x6c) {    /* Byte order LSB first. */
        !            92:                proto_len = ucp[6] + 256 * ucp[7];
        !            93:                data_len = ucp[8] + 256 * ucp[9];
        !            94:        } else {
        !            95:                debug("Initial X11 packet contains bad byte order byte: 0x%x",
        !            96:                      ucp[0]);
        !            97:                return -1;
        !            98:        }
        !            99:
        !           100:        /* Check if the whole packet is in buffer. */
        !           101:        if (buffer_len(b) <
        !           102:            12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
        !           103:                return 0;
        !           104:
        !           105:        /* Check if authentication protocol matches. */
        !           106:        if (proto_len != strlen(x11_saved_proto) ||
        !           107:            memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
        !           108:                debug("X11 connection uses different authentication protocol.");
        !           109:                return -1;
        !           110:        }
        !           111:        /* Check if authentication data matches our fake data. */
        !           112:        if (data_len != x11_fake_data_len ||
        !           113:            memcmp(ucp + 12 + ((proto_len + 3) & ~3),
        !           114:                x11_fake_data, x11_fake_data_len) != 0) {
        !           115:                debug("X11 auth data does not match fake data.");
        !           116:                return -1;
        !           117:        }
        !           118:        /* Check fake data length */
        !           119:        if (x11_fake_data_len != x11_saved_data_len) {
        !           120:                error("X11 fake_data_len %d != saved_data_len %d",
        !           121:                    x11_fake_data_len, x11_saved_data_len);
        !           122:                return -1;
        !           123:        }
        !           124:        /*
        !           125:         * Received authentication protocol and data match
        !           126:         * our fake data. Substitute the fake data with real
        !           127:         * data.
        !           128:         */
        !           129:        memcpy(ucp + 12 + ((proto_len + 3) & ~3),
        !           130:            x11_saved_data, x11_saved_data_len);
        !           131:        return 1;
        !           132: }
        !           133:
        !           134: /*
        !           135:  * Creates an internet domain socket for listening for X11 connections.
        !           136:  * Returns a suitable value for the DISPLAY variable, or NULL if an error
        !           137:  * occurs.
        !           138:  */
        !           139: char *
        !           140: x11_create_display_inet(int screen_number, int x11_display_offset)
        !           141: {
        !           142:        int display_number, sock;
        !           143:        u_short port;
        !           144:        struct addrinfo hints, *ai, *aitop;
        !           145:        char strport[NI_MAXSERV];
        !           146:        int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
        !           147:        char display[512];
        !           148:        char hostname[MAXHOSTNAMELEN];
        !           149:
        !           150:        for (display_number = x11_display_offset;
        !           151:             display_number < MAX_DISPLAYS;
        !           152:             display_number++) {
        !           153:                port = 6000 + display_number;
        !           154:                memset(&hints, 0, sizeof(hints));
        !           155:                hints.ai_family = IPv4or6;
        !           156:                hints.ai_flags = AI_PASSIVE;            /* XXX loopback only ? */
        !           157:                hints.ai_socktype = SOCK_STREAM;
        !           158:                snprintf(strport, sizeof strport, "%d", port);
        !           159:                if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
        !           160:                        error("getaddrinfo: %.100s", gai_strerror(gaierr));
        !           161:                        return NULL;
        !           162:                }
        !           163:                for (ai = aitop; ai; ai = ai->ai_next) {
        !           164:                        if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
        !           165:                                continue;
        !           166:                        sock = socket(ai->ai_family, SOCK_STREAM, 0);
        !           167:                        if (sock < 0) {
        !           168:                                error("socket: %.100s", strerror(errno));
        !           169:                                return NULL;
        !           170:                        }
        !           171:                        if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
        !           172:                                debug("bind port %d: %.100s", port, strerror(errno));
        !           173:                                shutdown(sock, SHUT_RDWR);
        !           174:                                close(sock);
        !           175:                                for (n = 0; n < num_socks; n++) {
        !           176:                                        shutdown(socks[n], SHUT_RDWR);
        !           177:                                        close(socks[n]);
        !           178:                                }
        !           179:                                num_socks = 0;
        !           180:                                break;
        !           181:                        }
        !           182:                        socks[num_socks++] = sock;
        !           183:                        if (num_socks == NUM_SOCKS)
        !           184:                                break;
        !           185:                }
        !           186:                freeaddrinfo(aitop);
        !           187:                if (num_socks > 0)
        !           188:                        break;
        !           189:        }
        !           190:        if (display_number >= MAX_DISPLAYS) {
        !           191:                error("Failed to allocate internet-domain X11 display socket.");
        !           192:                return NULL;
        !           193:        }
        !           194:        /* Start listening for connections on the socket. */
        !           195:        for (n = 0; n < num_socks; n++) {
        !           196:                sock = socks[n];
        !           197:                if (listen(sock, 5) < 0) {
        !           198:                        error("listen: %.100s", strerror(errno));
        !           199:                        shutdown(sock, SHUT_RDWR);
        !           200:                        close(sock);
        !           201:                        return NULL;
        !           202:                }
        !           203:        }
        !           204:
        !           205:        /* Set up a suitable value for the DISPLAY variable. */
        !           206:        if (gethostname(hostname, sizeof(hostname)) < 0)
        !           207:                fatal("gethostname: %.100s", strerror(errno));
        !           208:        snprintf(display, sizeof display, "%.400s:%d.%d", hostname,
        !           209:                 display_number, screen_number);
        !           210:
        !           211:        /* Allocate a channel for each socket. */
        !           212:        for (n = 0; n < num_socks; n++) {
        !           213:                sock = socks[n];
        !           214:                (void) channel_new("x11 listener",
        !           215:                    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
        !           216:                    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
        !           217:                    0, xstrdup("X11 inet listener"), 1);
        !           218:        }
        !           219:
        !           220:        /* Return a suitable value for the DISPLAY environment variable. */
        !           221:        return xstrdup(display);
        !           222: }
        !           223:
        !           224: #ifndef X_UNIX_PATH
        !           225: #define X_UNIX_PATH "/tmp/.X11-unix/X"
        !           226: #endif
        !           227:
        !           228: static
        !           229: int
        !           230: connect_local_xsocket(u_int dnr)
        !           231: {
        !           232:        static const char *const x_sockets[] = {
        !           233:                X_UNIX_PATH "%u",
        !           234:                "/var/X/.X11-unix/X" "%u",
        !           235:                "/usr/spool/sockets/X11/" "%u",
        !           236:                NULL
        !           237:        };
        !           238:        int sock;
        !           239:        struct sockaddr_un addr;
        !           240:        const char *const * path;
        !           241:
        !           242:        for (path = x_sockets; *path; ++path) {
        !           243:                sock = socket(AF_UNIX, SOCK_STREAM, 0);
        !           244:                if (sock < 0)
        !           245:                        error("socket: %.100s", strerror(errno));
        !           246:                memset(&addr, 0, sizeof(addr));
        !           247:                addr.sun_family = AF_UNIX;
        !           248:                snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr);
        !           249:                if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
        !           250:                        return sock;
        !           251:                close(sock);
        !           252:        }
        !           253:        error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
        !           254:        return -1;
        !           255: }
        !           256:
        !           257: int
        !           258: x11_connect_display(void)
        !           259: {
        !           260:        int display_number, sock = 0;
        !           261:        const char *display;
        !           262:        char buf[1024], *cp;
        !           263:        struct addrinfo hints, *ai, *aitop;
        !           264:        char strport[NI_MAXSERV];
        !           265:        int gaierr;
        !           266:
        !           267:        /* Try to open a socket for the local X server. */
        !           268:        display = getenv("DISPLAY");
        !           269:        if (!display) {
        !           270:                error("DISPLAY not set.");
        !           271:                return -1;
        !           272:        }
        !           273:        /*
        !           274:         * Now we decode the value of the DISPLAY variable and make a
        !           275:         * connection to the real X server.
        !           276:         */
        !           277:
        !           278:        /*
        !           279:         * Check if it is a unix domain socket.  Unix domain displays are in
        !           280:         * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
        !           281:         */
        !           282:        if (strncmp(display, "unix:", 5) == 0 ||
        !           283:            display[0] == ':') {
        !           284:                /* Connect to the unix domain socket. */
        !           285:                if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
        !           286:                        error("Could not parse display number from DISPLAY: %.100s",
        !           287:                              display);
        !           288:                        return -1;
        !           289:                }
        !           290:                /* Create a socket. */
        !           291:                sock = connect_local_xsocket(display_number);
        !           292:                if (sock < 0)
        !           293:                        return -1;
        !           294:
        !           295:                /* OK, we now have a connection to the display. */
        !           296:                return sock;
        !           297:        }
        !           298:        /*
        !           299:         * Connect to an inet socket.  The DISPLAY value is supposedly
        !           300:         * hostname:d[.s], where hostname may also be numeric IP address.
        !           301:         */
        !           302:        strncpy(buf, display, sizeof(buf));
        !           303:        buf[sizeof(buf) - 1] = 0;
        !           304:        cp = strchr(buf, ':');
        !           305:        if (!cp) {
        !           306:                error("Could not find ':' in DISPLAY: %.100s", display);
        !           307:                return -1;
        !           308:        }
        !           309:        *cp = 0;
        !           310:        /* buf now contains the host name.  But first we parse the display number. */
        !           311:        if (sscanf(cp + 1, "%d", &display_number) != 1) {
        !           312:                error("Could not parse display number from DISPLAY: %.100s",
        !           313:                      display);
        !           314:                return -1;
        !           315:        }
        !           316:
        !           317:        /* Look up the host address */
        !           318:        memset(&hints, 0, sizeof(hints));
        !           319:        hints.ai_family = IPv4or6;
        !           320:        hints.ai_socktype = SOCK_STREAM;
        !           321:        snprintf(strport, sizeof strport, "%d", 6000 + display_number);
        !           322:        if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
        !           323:                error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
        !           324:                return -1;
        !           325:        }
        !           326:        for (ai = aitop; ai; ai = ai->ai_next) {
        !           327:                /* Create a socket. */
        !           328:                sock = socket(ai->ai_family, SOCK_STREAM, 0);
        !           329:                if (sock < 0) {
        !           330:                        debug("socket: %.100s", strerror(errno));
        !           331:                        continue;
        !           332:                }
        !           333:                /* Connect it to the display. */
        !           334:                if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
        !           335:                        debug("connect %.100s port %d: %.100s", buf,
        !           336:                            6000 + display_number, strerror(errno));
        !           337:                        close(sock);
        !           338:                        continue;
        !           339:                }
        !           340:                /* Success */
        !           341:                break;
        !           342:        }
        !           343:        freeaddrinfo(aitop);
        !           344:        if (!ai) {
        !           345:                error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
        !           346:                    strerror(errno));
        !           347:                return -1;
        !           348:        }
        !           349:        return sock;
        !           350: }
        !           351:
        !           352: /*
        !           353:  * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
        !           354:  * the remote channel number.  We should do whatever we want, and respond
        !           355:  * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
        !           356:  */
        !           357:
        !           358: void
        !           359: x11_input_open(int type, int plen, void *ctxt)
        !           360: {
        !           361:        Channel *c = NULL;
        !           362:        int remote_id, sock = 0;
        !           363:        char *remote_host;
        !           364:
        !           365:        debug("Received X11 open request.");
        !           366:
        !           367:        remote_id = packet_get_int();
        !           368:
        !           369:        if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
        !           370:                remote_host = packet_get_string(NULL);
        !           371:        } else {
        !           372:                remote_host = xstrdup("unknown (remote did not supply name)");
        !           373:        }
        !           374:        packet_done();
        !           375:
        !           376:        /* Obtain a connection to the real X display. */
        !           377:        sock = x11_connect_display();
        !           378:        if (sock != -1) {
        !           379:                /* Allocate a channel for this connection. */
        !           380:                c = channel_new("connected x11 socket",
        !           381:                    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
        !           382:                    remote_host, 1);
        !           383:                if (c == NULL) {
        !           384:                        error("x11_input_open: channel_new failed");
        !           385:                        close(sock);
        !           386:                } else {
        !           387:                        c->remote_id = remote_id;
        !           388:                }
        !           389:        }
        !           390:        if (c == NULL) {
        !           391:                /* Send refusal to the remote host. */
        !           392:                packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
        !           393:                packet_put_int(remote_id);
        !           394:        } else {
        !           395:                /* Send a confirmation to the remote host. */
        !           396:                packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
        !           397:                packet_put_int(remote_id);
        !           398:                packet_put_int(c->self);
        !           399:        }
        !           400:        packet_send();
        !           401: }
        !           402:
        !           403: /* dummy protocol handler that denies SSH-1 requests (agent/x11) */
        !           404: void
        !           405: deny_input_open(int type, int plen, void *ctxt)
        !           406: {
        !           407:        int rchan = packet_get_int();
        !           408:        switch(type){
        !           409:        case SSH_SMSG_AGENT_OPEN:
        !           410:                error("Warning: ssh server tried agent forwarding.");
        !           411:                break;
        !           412:        case SSH_SMSG_X11_OPEN:
        !           413:                error("Warning: ssh server tried X11 forwarding.");
        !           414:                break;
        !           415:        default:
        !           416:                error("deny_input_open: type %d plen %d", type, plen);
        !           417:                break;
        !           418:        }
        !           419:        error("Warning: this is probably a break in attempt by a malicious server.");
        !           420:        packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
        !           421:        packet_put_int(rchan);
        !           422:        packet_send();
        !           423: }
        !           424:
        !           425: /*
        !           426:  * Requests forwarding of X11 connections, generates fake authentication
        !           427:  * data, and enables authentication spoofing.
        !           428:  * This should be called in the client only.
        !           429:  */
        !           430: void
        !           431: x11_request_forwarding_with_spoofing(int client_session_id,
        !           432:     const char *proto, const char *data)
        !           433: {
        !           434:        u_int data_len = (u_int) strlen(data) / 2;
        !           435:        u_int i, value, len;
        !           436:        char *new_data;
        !           437:        int screen_number;
        !           438:        const char *cp;
        !           439:        u_int32_t rand = 0;
        !           440:
        !           441:        cp = getenv("DISPLAY");
        !           442:        if (cp)
        !           443:                cp = strchr(cp, ':');
        !           444:        if (cp)
        !           445:                cp = strchr(cp, '.');
        !           446:        if (cp)
        !           447:                screen_number = atoi(cp + 1);
        !           448:        else
        !           449:                screen_number = 0;
        !           450:
        !           451:        /* Save protocol name. */
        !           452:        x11_saved_proto = xstrdup(proto);
        !           453:
        !           454:        /*
        !           455:         * Extract real authentication data and generate fake data of the
        !           456:         * same length.
        !           457:         */
        !           458:        x11_saved_data = xmalloc(data_len);
        !           459:        x11_fake_data = xmalloc(data_len);
        !           460:        for (i = 0; i < data_len; i++) {
        !           461:                if (sscanf(data + 2 * i, "%2x", &value) != 1)
        !           462:                        fatal("x11_request_forwarding: bad authentication data: %.100s", data);
        !           463:                if (i % 4 == 0)
        !           464:                        rand = arc4random();
        !           465:                x11_saved_data[i] = value;
        !           466:                x11_fake_data[i] = rand & 0xff;
        !           467:                rand >>= 8;
        !           468:        }
        !           469:        x11_saved_data_len = data_len;
        !           470:        x11_fake_data_len = data_len;
        !           471:
        !           472:        /* Convert the fake data into hex. */
        !           473:        len = 2 * data_len + 1;
        !           474:        new_data = xmalloc(len);
        !           475:        for (i = 0; i < data_len; i++)
        !           476:                snprintf(new_data + 2 * i, len - 2 * i,
        !           477:                    "%02x", (u_char) x11_fake_data[i]);
        !           478:
        !           479:        /* Send the request packet. */
        !           480:        if (compat20) {
        !           481:                channel_request_start(client_session_id, "x11-req", 0);
        !           482:                packet_put_char(0);     /* XXX bool single connection */
        !           483:        } else {
        !           484:                packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
        !           485:        }
        !           486:        packet_put_cstring(proto);
        !           487:        packet_put_cstring(new_data);
        !           488:        packet_put_int(screen_number);
        !           489:        packet_send();
        !           490:        packet_write_wait();
        !           491:        xfree(new_data);
        !           492: }