server_x11.c 7.83 KB
Newer Older
Niels Möller's avatar
Niels Möller committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* server_x11.c
 *
 */

/* lsh, an implementation of the ssh protocol
 *
 * Copyright (C) 2002 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

24
25
26
27
28
29
30
31
32
33
#if HAVE_CONFIG_H
#include "config.h"
#endif

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

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

34
35
36
37
#if HAVE_X11_XAUTH_H
#include <X11/Xauth.h>
#endif

38
39
40
41
42
43
#include <sys/types.h>

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

Niels Möller's avatar
Niels Möller committed
44
45
#include "server_x11.h"

46
#include "channel_forward.h"
47
#include "environ.h"
48
#include "format.h"
49
#include "io.h"
Niels Möller's avatar
Niels Möller committed
50
#include "lsh_string.h"
51
#include "lsh_process.h"
52
#include "reaper.h"
53
#include "resource.h"
54
#include "ssh.h"
Niels Möller's avatar
Niels Möller committed
55
#include "werror.h"
56
57
#include "xalloc.h"

58

59
60
61
62
#define GABA_DEFINE
#include "server_x11.h.x"
#undef GABA_DEFINE

Niels Möller's avatar
Niels Möller committed
63
#if WITH_X11_FORWARD
64

Niels Möller's avatar
Niels Möller committed
65
66
67
68
69
#ifndef SUN_LEN
# define SUN_LEN(x) \
  (offsetof(struct sockaddr_un, sun_path) + strlen((x)->sun_path))
#endif

70
71
#define X11_WINDOW_SIZE 10000

72
#define X11_MIN_COOKIE_LENGTH 10
73
74
75
76
77
78
79
#define X11_SOCKET_DIR "/tmp/.X11-unix"

/* The interval of display numbers that we use. */
#define X11_MIN_DISPLAY 10
#define X11_MAX_DISPLAY 1000

static void
80
do_kill_x11_listen_port(struct resource *s)
81
{
82
  CAST(x11_listen_port, self, s);
83
84
  int old_cd;

85
  if (self->super.super.super.alive)
86
    {
87
88
      self->super.super.super.alive = 0;
      io_close_fd(self->super.super.fd);
89
90
91
92
93
94
95
96
97
98

      assert(self->dir >= 0);

      /* Temporarily change to the right directory. */
      old_cd = lsh_pushd_fd(self->dir);
      if (old_cd < 0)
	return;

      close(self->dir);
      self->dir = -1;
99

100
101
102
      if (unlink(lsh_get_cstring(self->name)) < 0)
	werror("Failed to delete x11 socket %S: %e\n",
	       self->name, errno);
103
104
105
106
107

      lsh_popd(old_cd, X11_SOCKET_DIR);
    }
}

108
109
110
111
112
static void
do_x11_listen_port_accept(struct io_listen_port *s,
				 int fd,
				 socklen_t addr_length UNUSED,
				 const struct sockaddr *addr UNUSED)
113
{
114
115
116
117
118
119
120
  CAST(x11_listen_port, self, s);
  struct ssh_channel *channel;

  trace("x11_listen_port_accept\n");

  io_register_fd(fd, "forwarded X11 socket");
  if (self->single)
121
    KILL_RESOURCE(&self->super.super.super);
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

  channel = &make_channel_forward(fd, X11_WINDOW_SIZE)->super;

  /* NOTE: The request ought to include some reference to the
   * corresponding x11 request, but no such id is specified in the
   * protocol spec. */
  /* NOTE: The name "unix-domain" was suggested by Tatu in
   * <200011290813.KAA17106@torni.hel.fi.ssh.com> */
  if (!channel_open_new_type(self->connection, channel,
			     ATOM_LD(ATOM_X11),
			     "%z%i", "unix-domain", 0))
    {
      werror("tcpforward_listen_port: Allocating a local channel number failed.");
      KILL_RESOURCE(&channel->super);
    }
}

static struct x11_listen_port *
make_x11_listen_port(int dir, const struct lsh_string *name,
		     int display_number, int fd,
		     struct ssh_connection *connection,
		     int single)
{
  NEW(x11_listen_port, self);
  init_io_listen_port(&self->super, fd, do_x11_listen_port_accept);
147
  self->super.super.super.kill = do_kill_x11_listen_port;
148
149
150

  self->display = NULL;
  self->xauthority = NULL;
151
152
153
154

  self->dir = dir;
  self->name = name;
  self->display_number = display_number;
155
156
157

  self->connection = connection;
  self->single = single;
158
159
160
161

  return self;
}

162
/* FIXME: Create the /tmp/.X11-unix directory, if needed. Figure out
Niels Möller's avatar
Niels Möller committed
163
 * if and how we should use /tmp/.X17-lock. */
164

165
166
167
/* Creates a socket in tmp. Some duplication with io_bind_local in
   io.c, but sufficiently different that it doesn't seem practical to
   unify them. */
168
169
170
171
172
173
174
175
/* This code is quite paranoid in order to avoid symlink attacks when
 * creating the socket. Similar paranoia in xlib would be desirable,
 * but not realistic. However, most of this is not needed if the
 * sticky bit is set properly on the /tmp and /tmp/.X11-unix
 * directories. */
static struct x11_listen_port *
open_x11_socket(struct ssh_connection *connection,
		int single)
176
177
178
179
180
181
{
  int old_cd;
  int dir;
  mode_t old_umask;
  
  int number;
182
  int s;
183
  struct lsh_string *name = NULL;
Niels Möller's avatar
Niels Möller committed
184

185
186
187
188
189
190
191
192
  /* We have to change the umask, as that's the only way to control
   * the permissions that bind uses. */

  old_umask = umask(0077);
  
  old_cd = lsh_pushd(X11_SOCKET_DIR, &dir, 0, 0);
  if (old_cd < 0)
    {
193
      werror("Failed to cd to `%z' %e\n", X11_SOCKET_DIR, errno);
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

      umask(old_umask);
      return NULL;
    }
  
  for (number = X11_MIN_DISPLAY; number <= X11_MAX_DISPLAY; number++)
    {
      /* The default size if sockaddr_un should always be enough to format
       * the filename "X<display num>". */
      struct sockaddr_un sa;
  
      sa.sun_family = AF_UNIX;
      sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
      snprintf(sa.sun_path, sizeof(sa.sun_path), "X%d", number);

209
210
211
      s = io_bind_sockaddr((struct sockaddr *) &sa, SUN_LEN(&sa));

      if (s >= 0)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
	{
	  /* Store name */
	  name = ssh_format("%lz", sa.sun_path);
	  break;
	}
    }

  umask(old_umask);
  
  lsh_popd(old_cd, X11_SOCKET_DIR);

  if (!name)
    {
      /* Couldn't find any display */
      close(dir);
      
      return NULL;
    }
230

231
  return make_x11_listen_port(dir, name, number, s, connection, single);
232
233
}

234
235
static int
create_xauth(const char *file, Xauth *xa)
Niels Möller's avatar
Niels Möller committed
236
{
237
238
239
240
241
242
243
244
245
246
  FILE *f;
  int fd;
  int res;

  /* Is locking overkill? */
  if (XauLockAuth(file, 1, 1, 0) != LOCK_SUCCESS)
    return 0;

  fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  if (fd < 0)
247
    {
248
249
250
251
      werror("Opening xauth file %z failed %e\n", file, errno);
    fail:
      XauUnlockAuth(file);
      return 0;
252
    }
253
254
255
256
257
258
259
260
261
	    
  f = fdopen(fd, "wb");
  if (!f)
    {
      werror("fdopen of xauth file %z failed.\n", file);
      close(fd);
      goto fail;
    }
  res = XauWriteAuth(f, xa);
Niels Möller's avatar
Niels Möller committed
262

263
264
  if (fclose(f) != 0)
    res = 0;
265

266
267
  XauUnlockAuth(file);
  return res;
268
269
}

270
struct x11_listen_port *
271
server_x11_setup(struct ssh_channel *channel,
272
		 int single,
273
274
		 uint32_t protocol_length, const uint8_t *protocol,
		 uint32_t cookie_length, const uint8_t *cookie,
275
		 uint32_t screen)
276
{
277
  struct x11_listen_port *port;
278
  Xauth xa;  
279
  const char *tmp;
280
281
282
#define HOST_MAX 200
  char host[HOST_MAX];
  char number[10];
283

284
  if (cookie_length < X11_MIN_COOKIE_LENGTH)
285
286
287
288
289
    {
      werror("server_x11_setup: Cookie too small.\n");
      return NULL;
    }

290
291
292
  if (gethostname(host, sizeof(host) - 1) < 0)
    return 0;
  
293
  /* Get a free socket under /tmp/.X11-unix/ */
294
295
  port = open_x11_socket(channel->connection, single);
  if (!port)
296
297
    return NULL;

298
  tmp = getenv(ENV_TMPDIR);
299
  if (!tmp)
300
    tmp = "/tmp";
301

302
  port->xauthority = ssh_format("%lz/.lshd.%di.Xauthority", tmp, port->display_number);
303

304
  snprintf(number, sizeof(number), "%d", port->display_number);
305
306
307
308
309
310
311
312
313
314
315
  xa.family = FamilyLocal;
  xa.address_length = strlen(host);
  xa.address = host;
  xa.number_length = strlen(number);
  xa.number = number;
  xa.name_length = protocol_length;
  /* Casts needed since the Xauth pointers are non-const. */
  xa.name = (char *) protocol;
  xa.data_length = cookie_length;
  xa.data = (char *) cookie;

316
  if (!create_xauth(lsh_get_cstring(port->xauthority), &xa))
317
    {
318
      KILL_RESOURCE(&port->super.super.super);
319
320
321
322
      return NULL;
    }
  else if (!io_listen(&port->super))
    {
323
      KILL_RESOURCE(&port->super.super.super);
324
325
326
327
      return NULL;
    }
  else
    {
328
329
330
      port->display = ssh_format("unix:%di.%di",
				 port->display_number, screen);
      return port;
331
    }
332
333
}

Niels Möller's avatar
Niels Möller committed
334
#endif /* WITH_X11_FORWARD */