client_x11.c 22.2 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
/* client_x11.c
 *
3
 * Client side of X11 forwarding.
Niels Möller's avatar
Niels Möller committed
4
 *
5
 */
Niels Möller's avatar
Niels Möller committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 2001 Niels Mller
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <assert.h>
#include <string.h>

#include <fcntl.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/un.h>

#include <netinet/in.h>

41
42
43
44
45
#include <X11/X.h>
#if HAVE_X11_XAUTH_H
#include <X11/Xauth.h>
#endif

46
47
#include "nettle/macros.h"

Niels Möller's avatar
Niels Möller committed
48
49
50
51
#include "client.h"

#include "channel_forward.h"
#include "format.h"
52
#include "gateway.h"
53
#include "lsh_string.h"
Niels Möller's avatar
Niels Möller committed
54
55
56
#include "ssh.h"
#include "werror.h"
#include "xalloc.h"
Niels Möller's avatar
Niels Möller committed
57
58
59

#include "client_x11.c.x"

Niels Möller's avatar
Niels Möller committed
60
61
62
/* First port number for X, according to RFC 1013 */
#define X11_BASE_PORT 6000

63
64
#define X11_WINDOW_SIZE 10000

65
66
#define X11_COOKIE_LENGTH 16

Niels Möller's avatar
Niels Möller committed
67
68
/* GABA:
   (class
Niels Möller's avatar
Niels Möller committed
69
     (name client_x11_display)
70
     (super client_x11_handler)
Niels Möller's avatar
Niels Möller committed
71
72
73
74
     (vars
       (address_length . socklen_t)
       (address space "struct sockaddr")

Niels Möller's avatar
Niels Möller committed
75
       ; Default screen
76
       (screen . uint16_t)
77
78
79
80
81
82
83

       ; Fake MIT-COOKIE-1
       (fake string)

       ; Real authentication info
       (auth_name string)
       (auth_data string)))
Niels Möller's avatar
Niels Möller committed
84
*/
85

86
87
/* GABA:
   (class
Niels Möller's avatar
Niels Möller committed
88
     (name client_x11_channel)
89
90
     (super channel_forward)
     (vars
91
       (display object client_x11_display)
Niels Möller's avatar
Niels Möller committed
92
       (state . int)
93
94
95
       (little_endian . int)
       (name_length . unsigned)
       (auth_length . unsigned)
96
       (i . uint32_t)
97
       (buffer string)))
Niels Möller's avatar
Niels Möller committed
98
99
*/

Niels Möller's avatar
Niels Möller committed
100
101
102
103
104
105
106
107
108
/* This function is responsible for checking the fake cookie, and
 * replacing it with the real one.
 *
 * It intercepts the first packet sent by the client, which has the
 * following format:
 *
 * Type                        Possible or typical values
 *
 * uint8_t  byte-order         'B' (big endian) or 'L' (little endian)
109
110
 *                             According to Jean-Pierre, some servers
 *                             use 'b' and 'l' instead.
111
 * uint8_t  pad                0
Niels Möller's avatar
Niels Möller committed
112
 * uint16_t major-version      Usually 11
113
114
115
116
 * uint16_t minor-version      Usually 0.
 * uint16_t name_length        18
 * uint16_t auth_length        16
 * uint16_t pad                What's this?
Niels Möller's avatar
Niels Möller committed
117
118
119
 * uint8_t [name_length] name  "MIT-MAGIC-COOKIE-1"
 * uint8_t [auth_length] auth  Authentication data
 *
120
121
122
 * The last fields; name and auth, are padded to a multiple of four octets.
 *
 * The typical setup packet, with a 16-octet cookie, is 48 octets.
Niels Möller's avatar
Niels Möller committed
123
124
 */

125
/* Observed data:
126
127
128
129
130
131
132
133
 *
 *  $10 = {0x42, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x12, 0x0, 0x10, 0x0, 0xa4, 0x4d, 
 *  0x49, 0x54, 0x2d, 0x4d, 0x41, 0x47, 0x49, 0x43, 0x2d, 0x43, 0x4f, 0x4f, 
 *  0x4b, 0x49, 0x45, 0x2d, 0x31, 0xff, 0xf7, 0x8b, 0x1e, 0x2c, 0xa0, 0x98, 
 *  0x11, 0x27, 0x82, 0xa9, 0x0, 0x2d, 0xc4, 0x68, 0x7f, 0x66, 0x2b}
 *
 */

134
135
/* From Pike's X.pmod:
 *
136
 *     // Always uses network byteorder (big endian) 
137
138
139
140
141
142
 *     string msg = sprintf("B\0%2c%2c%2c%2c\0\0%s%s",
 * 			    11, 0,
 * 			    strlen(auth_data->name), strlen(auth_data->data),
 * 			    ._Xlib.pad(auth_data->name), ._Xlib.pad(auth_data->data));
 */

Niels Möller's avatar
Niels Möller committed
143
144
145
146
#define MIT_COOKIE_NAME "MIT-MAGIC-COOKIE-1"
#define MIT_COOKIE_NAME_LENGTH 18
#define MIT_COOKIE_LENGTH 16

147
148
149
150
151
#define X11_SETUP_VERSION_LENGTH 6
#define X11_SETUP_HEADER_LENGTH 12

/* The size of a connection setup message with a 16 octet
 * MIT-MAGIC-COOKIE-1. Using such a low value leaks the information
Niels Möller's avatar
Niels Möller committed
152
 * that we expect a 16-octet cookie, but I don't think that's a real
153
154
 * problem. */
#define X11_SETUP_MAX_LENGTH 48
Niels Möller's avatar
Niels Möller committed
155
156

enum { CLIENT_X11_START,
157
       CLIENT_X11_GOT_LENGTHS,
158
       CLIENT_X11_CANT_HAPPEN,
Niels Möller's avatar
Niels Möller committed
159
160
};

161
162
163
164
165
166
167
168
169
170
171
/* And the other, little-endian, byteorder */
#define LE_READ_UINT16(p)			\
(  (((uint32_t) (p)[1]) << 8)			\
 |  ((uint32_t) (p)[0]))

#define LE_WRITE_UINT16(p, i)			\
do {						\
  (p)[1] = ((i) >> 8) & 0xff;			\
  (p)[0] = (i) & 0xff;				\
} while(0)

Niels Möller's avatar
Niels Möller committed
172
173
static void
do_client_channel_x11_receive(struct ssh_channel *s,
174
175
                              int type,
			      uint32_t length, const uint8_t *data)
Niels Möller's avatar
Niels Möller committed
176
{
Niels Möller's avatar
Niels Möller committed
177
  CAST(client_x11_channel, self, s);
Niels Möller's avatar
Niels Möller committed
178

179
  if (type != CHANNEL_DATA)
180
181
182
    werror("Ignoring unexpected stderr data on X11 channel.\n");

  else
Niels Möller's avatar
Niels Möller committed
183
    {
184
185
      /* Copy data to buffer */
      const uint8_t *buffer;
186
187

      werror("client_x11.c: length = %i\n", length);
188
189
190
191
      /* The small initial window size should ensure that all the
	 data fits. */
      lsh_string_write(self->buffer, self->i, length, data);
      self->i += length;
192

193
      buffer = lsh_string_data(self->buffer);
Niels Möller's avatar
Niels Möller committed
194
	
195
196
197
198
199
200
201
202
      switch (self->state)
	{
	default:
	  fatal("Internal error. do_client_channel_x11_receive");
	  break;
	case CLIENT_X11_START:
	  /* We need byte-order, major, minor and name_length,
	   * which is 6 octets */
Niels Möller's avatar
Niels Möller committed
203
               
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
	  if (self->i < X11_SETUP_HEADER_LENGTH)
	    break;

	  self->state = CLIENT_X11_GOT_LENGTHS;
	  switch (buffer[0])
	    {
	    case 'B': /* Big endian */
	    case 'b':
	      self->little_endian = 0;
	      self->name_length = READ_UINT16(buffer + 6);
	      self->auth_length = READ_UINT16(buffer + 8);
	      break;
	    case 'L': /* Little endian */
	    case 'l':
	      self->little_endian = 1;
	      self->name_length = LE_READ_UINT16(buffer + 6);
	      self->auth_length = LE_READ_UINT16(buffer + 8);
	      break;
	    default:
	      werror("client_x11.c: Bad endian indicator.\n");
	      goto fail;
	    }
	  if ( (self->name_length > 20)
	       || (self->auth_length > 16) )
	    {
	      werror("client_x11.c: Too long auth name or cookie\n");
	      goto fail;
	    }
232
            
233
	  /* Fall through */
234
            
235
236
237
	case CLIENT_X11_GOT_LENGTHS:
	  {
	    const unsigned pad_length[4] = { 0, 3, 2, 1 };
238
239

#define PAD(l) (pad_length[ (l) % 4])
Niels Möller's avatar
Niels Möller committed
240
              
241
242
	    uint32_t auth_offset = X11_SETUP_HEADER_LENGTH
	      + self->name_length + PAD(self->name_length);
243

244
245
	    uint32_t length = auth_offset
	      + self->auth_length + PAD(self->auth_length); 
246
                
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
	    if (self->i < length)
	      break;

	    debug("Received cookie of type `%ps': %xs\n",
		  self->name_length, buffer + X11_SETUP_HEADER_LENGTH,
		  self->auth_length, buffer + auth_offset);

	    /* Ok, now we have the connection setup message. Check if it's ok. */
	    if ( (self->name_length == MIT_COOKIE_NAME_LENGTH)
		 && !memcmp(buffer + X11_SETUP_HEADER_LENGTH,
			    MIT_COOKIE_NAME, MIT_COOKIE_NAME_LENGTH)
		 && lsh_string_eq_l(self->display->fake,
				    self->auth_length,
				    buffer + auth_offset))
	      {
		struct lsh_string *msg;
		uint8_t lengths[4];
		static const uint8_t pad[3] = { 0, 0, 0 };
		uint32_t nlength = lsh_string_length(self->display->auth_name);
		uint32_t alength = lsh_string_length(self->display->auth_data);
267
		  
268
269
		/* Cookies match! */
		verbose("client_x11: Allowing X11 connection; cookies match.\n");
270
                  
271
272
273
274
275
276
277
278
279
280
281
		if (self->little_endian)
		  {
		    LE_WRITE_UINT16(lengths, nlength);
		    LE_WRITE_UINT16(lengths + 2, alength);
		  }
		else
		  {
		    WRITE_UINT16(lengths, nlength);
		    WRITE_UINT16(lengths + 2, alength);
		  }

282
283
284
		/* Construct the real setup message. (Perhaps it would
		 * be easier to build the message by hand than using
		 * ssh_format?) */
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
		msg = ssh_format("%ls%ls%c%c%lS%ls%lS%ls",
				 X11_SETUP_VERSION_LENGTH, buffer,
				 4, lengths,
				 0, 0,
				 self->display->auth_name,
				 PAD(nlength), pad,
				 self->display->auth_data,
				 self->i - length,
				 buffer + self->i);

		lsh_string_free(self->buffer);
		self->buffer = NULL;

		/* Bump window size */
		channel_start_receive(&self->super.super,
				      X11_WINDOW_SIZE - lsh_string_length(msg));

		/* Replaces receive method, so that we are not called again. */
		channel_forward_start_io(&self->super);

		debug("client_x11.c: Sending real X11 setup message: %xS\n",
		      msg);
307
                  
308
309
310
311
312
313
314
315
316
		/* Send real x11 connection setup message. */
		channel_forward_write(&self->super, STRING_LD(msg));
		lsh_string_free(msg);
		  
		self->state = CLIENT_X11_CANT_HAPPEN;
	      }
	    else
	      {
		werror("client_x11: X11 connection denied; bad cookie.\n");
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#define X11_ACCESS_DENIED_REASON "Access denied.\n"
#define X11_ACCESS_DENIED_REASON_WORDS (sizeof(X11_ACCESS_DENIED_REASON + 2)/3)
#define X11_ACCESS_DENIED_LENGTH 8 + 4 * X11_ACCESS_DENIED_REASON_WORDS
		
		if (self->super.super.send_window_size >= X11_ACCESS_DENIED_LENGTH
		    && self->super.super.send_max_packet >= X11_ACCESS_DENIED_LENGTH)
		  {
		    /* Report failure to X client. According to RFC 1013,

		         Received by the client at connection setup:
			   success: BOOL
			   protocol-major-version: CARD16
			   protocol-minor-version: CARD16
			   length: CARD16
		     
			 Additional data received if authorization fails:
			   reason: STRING8

			 It seems that both the success boolean is
			 padded to 16 bits, and the reason string is
			 terminated by newline, with no additional
			 length field. From strace, using
			 little-endian byte-order:

			   read(3, "\0\26\v\0\0\0\6\0"..., 8)      = 8
			   read(3, "No protocol specified\n\27\10"..., 24) = 24
			   write(2, "No protocol specified\n"..., 22No protocol specified

			 Hence, use the following format:

			 offset name contents
			 0      success  0, 0
			 2      major    11 (depending on byte-order)
			 4      minor    0, 0
			 6      length   4  (depending on byte-order)
			 8      reason   "Access denied.\n\0" (16 bytes)
			 24     end
		    */

		    uint8_t msg[X11_ACCESS_DENIED_LENGTH];
		    memset(msg, 0, sizeof(msg));

		    if (self->little_endian)
		      {
			LE_WRITE_UINT16(msg + 2, 11);
			LE_WRITE_UINT16(msg + 4, 4);
		      }
		    else
		      {
			WRITE_UINT16(msg + 2, 11);
			WRITE_UINT16(msg + 4, 4);
		      }

		    memcpy(msg + 8, X11_ACCESS_DENIED_REASON, sizeof(X11_ACCESS_DENIED_REASON) - 1);
		    channel_transmit_data(&self->super.super, sizeof(msg), msg);
		    channel_eof(&self->super.super);
		  }
374
375
376
377
378
	      fail:
		channel_close(&self->super.super);
		self->state = CLIENT_X11_CANT_HAPPEN;
	      }
	    break;
379
#undef PAD
380
381
	  }
	}
Niels Möller's avatar
Niels Möller committed
382
383
384
    }
}

Niels Möller's avatar
Niels Möller committed
385
static struct client_x11_channel *
386
make_client_x11_channel(int fd,
387
			struct client_x11_display *display)
Niels Möller's avatar
Niels Möller committed
388
389
390
{
  NEW(client_x11_channel, self);

Niels Möller's avatar
Niels Möller committed
391
  /* Use a limited window size for the setup */
392
393
394
  init_channel_forward(&self->super, fd, X11_WINDOW_SIZE, NULL);

  self->super.super.rec_window_size = X11_SETUP_MAX_LENGTH;
395
396
  self->super.super.receive = do_client_channel_x11_receive;

397
  self->display = display;
Niels Möller's avatar
Niels Möller committed
398
399
  self->state = 0;
  self->buffer = lsh_string_alloc(X11_SETUP_MAX_LENGTH);
Niels Möller's avatar
Niels Möller committed
400
401

  return self;
Niels Möller's avatar
Niels Möller committed
402
403
}

Niels Möller's avatar
Niels Möller committed
404
405
/* GABA:
   (class
406
407
     (name x11_connect_state)
     (super io_connect_state)
Niels Möller's avatar
Niels Möller committed
408
     (vars
409
       (display object client_x11_display)
410
       (info const object channel_open_info)))
Niels Möller's avatar
Niels Möller committed
411
412
413
*/

static void
414
x11_connect_done(struct io_connect_state *s, int fd)
Niels Möller's avatar
Niels Möller committed
415
{
416
  CAST(x11_connect_state, self, s);
Niels Möller's avatar
Niels Möller committed
417

418
419
420
421
  struct client_x11_channel *channel
    = make_client_x11_channel(fd, self->display);
  
  channel_open_confirm(self->info, &channel->super.super);
Niels Möller's avatar
Niels Möller committed
422
423
}

424
425
426
427
428
429
430
431
/* Identical to tcpforward_connect_error. Unify? */
static void
x11_connect_error(struct io_connect_state *s, int error)
{
  CAST(x11_connect_state, self, s);
  
  werror("Connection failed, socket error %i\n", error);
  channel_open_deny(self->info,
432
		    SSH_OPEN_CONNECT_FAILED, "Connection failed");
Niels Möller's avatar
Niels Möller committed
433
434
}

435
436
437
static struct resource *
x11_connect(struct client_x11_display *display,
	    const struct channel_open_info *info)
Niels Möller's avatar
Niels Möller committed
438
{
439
440
441
442
443
444
445
446
447
448
  NEW(x11_connect_state, self);

  init_io_connect_state(&self->super,
			x11_connect_done,
			x11_connect_error);

  self->display = display;
  self->info = info;
  
  if (!io_connect(&self->super, display->address_length, display->address))
Niels Möller's avatar
Niels Möller committed
449
    {
450
      werror("Connecting to X11 server failed: %e.\n", errno);
451
      return NULL;
Niels Möller's avatar
Niels Möller committed
452
    }
453
  return &self->super.super.super;
Niels Möller's avatar
Niels Möller committed
454
455
}

456
DEFINE_CHANNEL_OPEN(channel_open_x11)
457
458
459
	(struct channel_open *s UNUSED,
	 const struct channel_open_info *info,
	 struct simple_buffer *args)
Niels Möller's avatar
Niels Möller committed
460
{
461
  CAST(client_connection, connection, info->connection);
462
  CAST_SUBTYPE(client_x11_handler, handler,
463
       resource_list_top(connection->x11_displays));
Niels Möller's avatar
Niels Möller committed
464

465
  if (handler)
Niels Möller's avatar
Niels Möller committed
466
    {
467
      if (handler->single_connection)
468
469
	KILL_RESOURCE(&handler->super);

470
471
      handler->open(handler, info, args);
    }
Niels Möller's avatar
Niels Möller committed
472
  else
473
474
475
    channel_open_deny(info,
		      SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
		      "Unexpected x11 request");
Niels Möller's avatar
Niels Möller committed
476
477
478
}


479
480
481
/* Setting up the forwarding. */
void
client_add_x11_handler(struct client_connection *connection,
482
		       struct client_x11_handler *handler)
483
{
484
  remember_resource(connection->x11_displays, &handler->super);
485
486
}

487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
static int
xauth_lookup(struct sockaddr *sa,
             unsigned number_length,
             const char *number,
             struct lsh_string **name,
             struct lsh_string **data)
{
#if HAVE_LIBXAU

  int res = 0;
  unsigned family;

  const char *address;
  unsigned address_length;
  
#define HOST_MAX 200
  char host[HOST_MAX];
  
  const char *filename = XauFileName();
  Xauth *xa;

  if (!filename)
    return 0;

  switch(sa->sa_family)
    {
    case AF_UNIX:
      if (gethostname(host, sizeof(host) - 1) < 0)
	return 0;
      address = host;
      address_length = strlen(host);
      family = FamilyLocal;
      break;

    case AF_INET:
      {
	struct sockaddr_in *s = (struct sockaddr_in *) sa;
	
	address = (char *) &s->sin_addr;
	address_length = 4;
	family = FamilyInternet;
	break;
      }

#if WITH_IPV6
    case AF_INET6:
      {
	struct sockaddr_in6 *s = (struct sockaddr_in6 *) sa;
	
	address = (char *) &s->sin6_addr;
	address_length = 16;
	family = FamilyInternet6;
	break;
      }
#endif
    default:
      return 0;
    }

  /* 5 retries, 1 second each */
  if (XauLockAuth(filename, 5, 1, 0) != LOCK_SUCCESS)
    return 0;

  /* NOTE: The man page doesn't list the last two arguments,
     name_length and name. From the source, it seems that a zero
     name_length means match any name. */
  xa = XauGetAuthByAddr(family, address_length, address,
			number_length, number, 0, "");
  if (xa)
    {
      debug("xauth: family: %i\n", xa->family);
      debug("       address: %ps\n", xa->address_length, xa->address);
      debug("       display: %s\n", xa->number_length, xa->number);
      debug("       name: %s\n", xa->name_length, xa->name);
      debug("       data length: %i\n", xa->data_length);

      *name = ssh_format("%ls", xa->name_length, xa->name);
      *data = ssh_format("%ls", xa->data_length, xa->data);

      XauDisposeAuth(xa);
      res = 1;
    }
  else
    res = 0;

  XauUnlockAuth(filename);
  return res;
#else /* !HAVE_LIBXAU */
  return 0;
#endif /* !HAVE_LIBXAU */
}

Niels Möller's avatar
Niels Möller committed
579
/* Format is host:display.screen, where display and screen are numbers */
580
581
static int
parse_display(struct client_x11_display *self, const char *display)
Niels Möller's avatar
Niels Möller committed
582
583
{
  struct lsh_string *host;
584
585
586

  const char *num;
  unsigned num_length;
Niels Möller's avatar
Niels Möller committed
587
  unsigned display_num;
Niels Möller's avatar
Niels Möller committed
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  
  /* Get host name */
  if (display[0] == ':')
    {
      /* Local transport */
      host = NULL;
      display++;
    }
  else
    {
      const char *separator = strchr(display, ':');
      size_t length;

      if (!separator)
602
	return 0;
Niels Möller's avatar
Niels Möller committed
603

Niels Möller's avatar
Niels Möller committed
604
      length = separator - display;
Pontus Freyhult's avatar
Pontus Freyhult committed
605
606
607
608
609
610

      if (!strncmp("unix", display, length)) /* Special name unix? */
	host = NULL; /* Local transport */
      else
 	host = ssh_format("%ls", length, display);

Niels Möller's avatar
Niels Möller committed
611
612
613
614
615
      display = separator + 1;
    }
  
  /* Get display number */
  {
616
    char *end;
617
618
    num = display;
    
Niels Möller's avatar
Niels Möller committed
619
620
    display_num = strtol(display, &end, 0);

621
622
623
    num_length = end - num;

    if (!num_length)
Niels Möller's avatar
Niels Möller committed
624
625
      {
	lsh_string_free(host);
626
	return 0;
Niels Möller's avatar
Niels Möller committed
627
      }
628
    
Niels Möller's avatar
Niels Möller committed
629
630
    if (!*end)
      /* Default screen number */
631
      self->screen = 0;
Niels Möller's avatar
Niels Möller committed
632
633
634
    else if (*end != '.')
      {
	lsh_string_free(host);
635
	return 0;
Niels Möller's avatar
Niels Möller committed
636
      }
637
    else
Niels Möller's avatar
Niels Möller committed
638
      {
639
640
641
642
643
644
645
646
        display = end + 1;
        self->screen = strtol(display, &end, 0);

        if (*end)
          {
            lsh_string_free(host);
            return 0;
          }
Niels Möller's avatar
Niels Möller committed
647
648
      }
  }
649
  
Niels Möller's avatar
Niels Möller committed
650
651
  if (host)
    {
652
653
654
655
656
657
      /* FIXME: Some duplication with io_lookup_address. */
      struct addrinfo hints;
      struct addrinfo *list;
      int err;
      char service[10];

Niels Möller's avatar
Niels Möller committed
658
      /* FIXME: uses IPv4 only. */
659
660
661
662
663
      memset(&hints, 0, sizeof(hints));
      hints.ai_family = AF_INET;
      hints.ai_socktype = SOCK_STREAM;

      snprintf(service, sizeof(service), "%d", X11_BASE_PORT + display_num);
Niels Möller's avatar
Niels Möller committed
664
      
665
666
      err = getaddrinfo(lsh_get_cstring(host), service, &hints, &list);
      lsh_string_free(host);
Niels Möller's avatar
Niels Möller committed
667

668
669
670
671
672
673
674
675
676
677
      if (err)
	{
	  werror("parse_display: getaddrinfo failed: %z\n",
		 gai_strerror(err));
	  return 0;
	}
      self->address_length = list->ai_addrlen;
      
      self->address = lsh_space_alloc(self->address_length);
      memcpy(self->address, list->ai_addr, self->address_length);
Niels Möller's avatar
Niels Möller committed
678

679
      freeaddrinfo(list);      
Niels Möller's avatar
Niels Möller committed
680
681
682
683
    }
  else
    {
      /* Local transport */
684
      struct lsh_string *name = ssh_format("/tmp/.X11-unix/X%di", display_num);
685
      uint32_t nlength = lsh_string_length(name);
Niels Möller's avatar
Niels Möller committed
686
687
      struct sockaddr_un *sa;

688
689
      verbose("Using local X11 transport `%pS'\n", name);
      
690
      self->address_length = offsetof(struct sockaddr_un, sun_path) + nlength;
691
      sa = lsh_space_alloc(self->address_length);
Niels Möller's avatar
Niels Möller committed
692
      sa->sun_family = AF_UNIX;
693
      memcpy(sa->sun_path, lsh_string_data(name), nlength);
Niels Möller's avatar
Niels Möller committed
694
695

      lsh_string_free(name);
696
      self->address = (struct sockaddr *) sa;
Niels Möller's avatar
Niels Möller committed
697
698
    }

699
  if (!xauth_lookup(self->address,
700
701
702
                    num_length, num,
                    &self->auth_name,
                    &self->auth_data))
Niels Möller's avatar
Niels Möller committed
703
704
705
706
707
    {
      /* Fallback: Don't use xauth, and hope that the X server uses
       * xhost to let us in anyway. */
      werror("Can't find any xauth information for X11 display.\n");

708
709
      self->auth_name = ssh_format("");
      self->auth_data = ssh_format("");
Niels Möller's avatar
Niels Möller committed
710
    }
711
712
  
  return 1;
Niels Möller's avatar
Niels Möller committed
713
}
Niels Möller's avatar
Niels Möller committed
714

715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
static void
do_client_x11_display_open(struct client_x11_handler *s,
			   const struct channel_open_info *info,
			   struct simple_buffer *args)
{
  CAST(client_x11_display, self, s);

  uint32_t originator_length;
  const uint8_t *originator;
  uint32_t originator_port;

  if (parse_string(args, &originator_length, &originator)
      && parse_uint32(args, &originator_port) 
      && parse_eod(args))
    {
      struct resource *r;

      verbose("x11 connection attempt, originator: %s:%i\n",
	      originator_length, originator, originator_port);

      r = x11_connect(self, info);
      if (r)
	remember_resource(info->connection->resources, r);
738
739
740
      else
	channel_open_deny(info,
			  SSH_OPEN_CONNECT_FAILED, "Connection failed");	
741
742
743
744
745
746
747
748
749
    }
  else
    {
      werror("do_client_x11_display_open: Invalid message!\n");
	  
      SSH_CONNECTION_ERROR(info->connection, "Invalid CHANNEL_OPEN x11 message.");
    }
}

750
static struct client_x11_display *
751
make_client_x11_display(const char *display, int single_connection)
Niels Möller's avatar
Niels Möller committed
752
{
Niels Möller's avatar
Niels Möller committed
753
  NEW(client_x11_display, self);
754
755
756
757
  init_resource(&self->super.super, NULL);

  self->super.single_connection = single_connection;
  self->super.open = do_client_x11_display_open;
758
  self->fake = NULL;
759

760
  if (!parse_display(self, display))
Niels Möller's avatar
Niels Möller committed
761
    {
762
      werror("Can't parse X11 display: `%z'\n", display);
763
764

      KILL_RESOURCE(&self->super.super);
Niels Möller's avatar
Niels Möller committed
765
766
767
      KILL(self);
      return NULL;
    }
768
  
Niels Möller's avatar
Niels Möller committed
769
  return self;
Niels Möller's avatar
Niels Möller committed
770
771
}

Niels Möller's avatar
Niels Möller committed
772
773
/* GABA:
   (class
774
775
     (name client_x11_fake_handler)
     (super client_random_handler)
Niels Möller's avatar
Niels Möller committed
776
     (vars
777
       (session object client_session)
Niels Möller's avatar
Niels Möller committed
778
779
       (display object client_x11_display)))
*/
Niels Möller's avatar
Niels Möller committed
780
781

static void
782
783
784
do_client_x11_handle_random_reply(struct client_random_handler *s,
				  uint32_t length,
				  const uint8_t *data)
Niels Möller's avatar
Niels Möller committed
785
{
786
  CAST(client_x11_fake_handler, self, s);
Niels Möller's avatar
Niels Möller committed
787

788
  assert(!self->display->fake);
Niels Möller's avatar
Niels Möller committed
789

790
791
  debug("do_client_x11_handle_random_reply: Fake cookie is %xs\n",
	length, data);
Niels Möller's avatar
Niels Möller committed
792

793
794
795
796
797
798
799
800
801
  if (length != X11_COOKIE_LENGTH)
    {
      werror("Unexpected length %i in RANDOM_REPLY for fake X11 cookie.\n",
	     length);
      channel_close(&self->session->super);
    }
  else
    {
      self->display->fake = ssh_format("%ls", length, data);
Niels Möller's avatar
Niels Möller committed
802

Niels Möller's avatar
Niels Möller committed
803
      /* NOTE: The cookie is hex encoded, presumably so that it can be
804
	 passed directly to the xauth command line. That's ugly, but
Niels Möller's avatar
Niels Möller committed
805
	 it's what the specification says. */
806
      
807
      if (!channel_send_request(&self->session->super, ATOM_LD(ATOM_X11_REQ), 1,
808
809
810
811
812
813
814
				"%c%s%xS%i",
				0, /* FIXME: Single connection not supported */
				MIT_COOKIE_NAME_LENGTH, MIT_COOKIE_NAME,
				self->display->fake,
				self->display->screen))
	werror("Session channel was closed in the middle of x11 forwarding setup.");
    }
Niels Möller's avatar
Niels Möller committed
815
816
}

Niels Möller's avatar
Niels Möller committed
817
818
/* GABA:
   (class
819
820
     (name client_x11_action)
     (super client_session_action)
Niels Möller's avatar
Niels Möller committed
821
     (vars
822
       (display object client_x11_display)))
Niels Möller's avatar
Niels Möller committed
823
824
*/

Niels Möller's avatar
Niels Möller committed
825
static void
826
827
do_action_x11_start(struct client_session_action *s,
		    struct client_session *session)
Niels Möller's avatar
Niels Möller committed
828
{
829
830
  CAST(client_x11_action, self, s);
  CAST(client_connection, connection, session->super.connection);
Niels Möller's avatar
Niels Möller committed
831

832
833
834
  NEW(client_x11_fake_handler, handler);
  handler->super.gateway = NULL;
  handler->super.reply = do_client_x11_handle_random_reply;
Niels Möller's avatar
Niels Möller committed
835

836
837
  handler->session = session;
  handler->display = self->display;
Niels Möller's avatar
Niels Möller committed
838

839
840
  client_random_request(connection,
			X11_COOKIE_LENGTH, &handler->super);  
Niels Möller's avatar
Niels Möller committed
841
}
842
843
844
845
       
static void
do_action_x11_success(struct client_session_action *s,
		      struct client_session *session)
Niels Möller's avatar
Niels Möller committed
846
{
847
848
  CAST(client_x11_action, self, s);
  CAST(client_connection, connection, session->super.connection);
Niels Möller's avatar
Niels Möller committed
849

850
  verbose("X11 request succeeded\n");
Niels Möller's avatar
Niels Möller committed
851

852
853
  client_add_x11_handler(connection, &self->display->super);
  session->x11 = &self->display->super.super;
Niels Möller's avatar
Niels Möller committed
854
855
}

856
857
858
static int
do_action_x11_failure(struct client_session_action *s UNUSED,
		      struct client_session *session UNUSED)
Niels Möller's avatar
Niels Möller committed
859
{
860
  verbose("x11 request failed\n");
Niels Möller's avatar
Niels Möller committed
861

862
  return 1;
Niels Möller's avatar
Niels Möller committed
863
}
Niels Möller's avatar
Niels Möller committed
864

865
struct client_session_action *
866
make_x11_action(const char *display_string, int single_connection)
Niels Möller's avatar
Niels Möller committed
867
{
868
869
  struct client_x11_display *display
    = make_client_x11_display(display_string, single_connection);
Niels Möller's avatar
Niels Möller committed
870
871
872

  if (display)
    {
873
874
875
876
877
      NEW(client_x11_action, self);
      self->super.serial = 0;
      self->super.start = do_action_x11_start;
      self->super.success = do_action_x11_success;
      self->super.failure = do_action_x11_failure;
Niels Möller's avatar
Niels Möller committed
878
879

      self->display = display;
880
881

      return &self->super;
Niels Möller's avatar
Niels Möller committed
882
883
884
    }
  else
    return NULL;
Niels Möller's avatar
Niels Möller committed
885
}