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