| 1 | /*
|
|---|
| 2 | * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
|
|---|
| 3 | * (Royal Institute of Technology, Stockholm, Sweden).
|
|---|
| 4 | * All rights reserved.
|
|---|
| 5 | *
|
|---|
| 6 | * Redistribution and use in source and binary forms, with or without
|
|---|
| 7 | * modification, are permitted provided that the following conditions
|
|---|
| 8 | * are met:
|
|---|
| 9 | *
|
|---|
| 10 | * 1. Redistributions of source code must retain the above copyright
|
|---|
| 11 | * notice, this list of conditions and the following disclaimer.
|
|---|
| 12 | *
|
|---|
| 13 | * 2. Redistributions in binary form must reproduce the above copyright
|
|---|
| 14 | * notice, this list of conditions and the following disclaimer in the
|
|---|
| 15 | * documentation and/or other materials provided with the distribution.
|
|---|
| 16 | *
|
|---|
| 17 | * 3. Neither the name of the Institute nor the names of its contributors
|
|---|
| 18 | * may be used to endorse or promote products derived from this software
|
|---|
| 19 | * without specific prior written permission.
|
|---|
| 20 | *
|
|---|
| 21 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|---|
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|---|
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|---|
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|---|
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|---|
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|---|
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|---|
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|---|
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|---|
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|---|
| 31 | * SUCH DAMAGE.
|
|---|
| 32 | */
|
|---|
| 33 |
|
|---|
| 34 | #include "kx.h"
|
|---|
| 35 |
|
|---|
| 36 | RCSID("$Id$");
|
|---|
| 37 |
|
|---|
| 38 | char x_socket[MaxPathLen];
|
|---|
| 39 |
|
|---|
| 40 | uint32_t display_num;
|
|---|
| 41 | char display[MaxPathLen];
|
|---|
| 42 | int display_size = sizeof(display);
|
|---|
| 43 | char xauthfile[MaxPathLen];
|
|---|
| 44 | int xauthfile_size = sizeof(xauthfile);
|
|---|
| 45 | u_char cookie[16];
|
|---|
| 46 | size_t cookie_len = sizeof(cookie);
|
|---|
| 47 |
|
|---|
| 48 | #ifndef X_UNIX_PATH
|
|---|
| 49 | #define X_UNIX_PATH "/tmp/.X11-unix/X"
|
|---|
| 50 | #endif
|
|---|
| 51 |
|
|---|
| 52 | #ifndef X_PIPE_PATH
|
|---|
| 53 | #define X_PIPE_PATH "/tmp/.X11-pipe/X"
|
|---|
| 54 | #endif
|
|---|
| 55 |
|
|---|
| 56 | /*
|
|---|
| 57 | * Allocate a unix domain socket in `s' for display `dpy' and with
|
|---|
| 58 | * filename `pattern'
|
|---|
| 59 | *
|
|---|
| 60 | * 0 if all is OK
|
|---|
| 61 | * -1 if bind failed badly
|
|---|
| 62 | * 1 if dpy is already used */
|
|---|
| 63 |
|
|---|
| 64 | static int
|
|---|
| 65 | try_socket (struct x_socket *s, int dpy, const char *pattern)
|
|---|
| 66 | {
|
|---|
| 67 | struct sockaddr_un addr;
|
|---|
| 68 | int fd;
|
|---|
| 69 |
|
|---|
| 70 | fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|---|
| 71 | if (fd < 0)
|
|---|
| 72 | err (1, "socket AF_UNIX");
|
|---|
| 73 | memset (&addr, 0, sizeof(addr));
|
|---|
| 74 | addr.sun_family = AF_UNIX;
|
|---|
| 75 | snprintf (addr.sun_path, sizeof(addr.sun_path), pattern, dpy);
|
|---|
| 76 | if(bind(fd,
|
|---|
| 77 | (struct sockaddr *)&addr,
|
|---|
| 78 | sizeof(addr)) < 0) {
|
|---|
| 79 | close (fd);
|
|---|
| 80 | if (errno == EADDRINUSE ||
|
|---|
| 81 | errno == EACCES /* Cray return EACCESS */
|
|---|
| 82 | #ifdef ENOTUNIQ
|
|---|
| 83 | || errno == ENOTUNIQ /* bug in Solaris 2.4 */
|
|---|
| 84 | #endif
|
|---|
| 85 | )
|
|---|
| 86 | return 1;
|
|---|
| 87 | else
|
|---|
| 88 | return -1;
|
|---|
| 89 | }
|
|---|
| 90 | s->fd = fd;
|
|---|
| 91 | s->pathname = strdup (addr.sun_path);
|
|---|
| 92 | if (s->pathname == NULL)
|
|---|
| 93 | errx (1, "strdup: out of memory");
|
|---|
| 94 | s->flags = UNIX_SOCKET;
|
|---|
| 95 | return 0;
|
|---|
| 96 | }
|
|---|
| 97 |
|
|---|
| 98 | #ifdef MAY_HAVE_X11_PIPES
|
|---|
| 99 | /*
|
|---|
| 100 | * Allocate a stream (masqueraded as a named pipe)
|
|---|
| 101 | *
|
|---|
| 102 | * 0 if all is OK
|
|---|
| 103 | * -1 if bind failed badly
|
|---|
| 104 | * 1 if dpy is already used
|
|---|
| 105 | */
|
|---|
| 106 |
|
|---|
| 107 | static int
|
|---|
| 108 | try_pipe (struct x_socket *s, int dpy, const char *pattern)
|
|---|
| 109 | {
|
|---|
| 110 | char path[MAXPATHLEN];
|
|---|
| 111 | int ret;
|
|---|
| 112 | int fd;
|
|---|
| 113 | int pipefd[2];
|
|---|
| 114 |
|
|---|
| 115 | snprintf (path, sizeof(path), pattern, dpy);
|
|---|
| 116 | fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
|---|
| 117 | if (fd < 0) {
|
|---|
| 118 | if (errno == EEXIST)
|
|---|
| 119 | return 1;
|
|---|
| 120 | else
|
|---|
| 121 | return -1;
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | close (fd);
|
|---|
| 125 |
|
|---|
| 126 | ret = pipe (pipefd);
|
|---|
| 127 | if (ret < 0)
|
|---|
| 128 | err (1, "pipe");
|
|---|
| 129 |
|
|---|
| 130 | ret = ioctl (pipefd[1], I_PUSH, "connld");
|
|---|
| 131 | if (ret < 0) {
|
|---|
| 132 | if(errno == ENOSYS)
|
|---|
| 133 | return -1;
|
|---|
| 134 | err (1, "ioctl I_PUSH");
|
|---|
| 135 | }
|
|---|
| 136 |
|
|---|
| 137 | ret = fattach (pipefd[1], path);
|
|---|
| 138 | if (ret < 0)
|
|---|
| 139 | err (1, "fattach %s", path);
|
|---|
| 140 |
|
|---|
| 141 | s->fd = pipefd[0];
|
|---|
| 142 | close (pipefd[1]);
|
|---|
| 143 | s->pathname = strdup (path);
|
|---|
| 144 | if (s->pathname == NULL)
|
|---|
| 145 | errx (1, "strdup: out of memory");
|
|---|
| 146 | s->flags = STREAM_PIPE;
|
|---|
| 147 | return 0;
|
|---|
| 148 | }
|
|---|
| 149 | #endif /* MAY_HAVE_X11_PIPES */
|
|---|
| 150 |
|
|---|
| 151 | /*
|
|---|
| 152 | * Try to create a TCP socket in `s' corresponding to display `dpy'.
|
|---|
| 153 | *
|
|---|
| 154 | * 0 if all is OK
|
|---|
| 155 | * -1 if bind failed badly
|
|---|
| 156 | * 1 if dpy is already used
|
|---|
| 157 | */
|
|---|
| 158 |
|
|---|
| 159 | static int
|
|---|
| 160 | try_tcp (struct x_socket *s, int dpy)
|
|---|
| 161 | {
|
|---|
| 162 | struct sockaddr_in tcpaddr;
|
|---|
| 163 | struct in_addr local;
|
|---|
| 164 | int one = 1;
|
|---|
| 165 | int fd;
|
|---|
| 166 |
|
|---|
| 167 | memset(&local, 0, sizeof(local));
|
|---|
| 168 | local.s_addr = htonl(INADDR_LOOPBACK);
|
|---|
| 169 |
|
|---|
| 170 | fd = socket (AF_INET, SOCK_STREAM, 0);
|
|---|
| 171 | if (fd < 0)
|
|---|
| 172 | err (1, "socket AF_INET");
|
|---|
| 173 | #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
|
|---|
| 174 | setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
|
|---|
| 175 | sizeof(one));
|
|---|
| 176 | #endif
|
|---|
| 177 | memset (&tcpaddr, 0, sizeof(tcpaddr));
|
|---|
| 178 | tcpaddr.sin_family = AF_INET;
|
|---|
| 179 | tcpaddr.sin_addr = local;
|
|---|
| 180 | tcpaddr.sin_port = htons(6000 + dpy);
|
|---|
| 181 | if (bind (fd, (struct sockaddr *)&tcpaddr,
|
|---|
| 182 | sizeof(tcpaddr)) < 0) {
|
|---|
| 183 | close (fd);
|
|---|
| 184 | if (errno == EADDRINUSE)
|
|---|
| 185 | return 1;
|
|---|
| 186 | else
|
|---|
| 187 | return -1;
|
|---|
| 188 | }
|
|---|
| 189 | s->fd = fd;
|
|---|
| 190 | s->pathname = NULL;
|
|---|
| 191 | s->flags = TCP;
|
|---|
| 192 | return 0;
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | /*
|
|---|
| 196 | * The potential places to create unix sockets.
|
|---|
| 197 | */
|
|---|
| 198 |
|
|---|
| 199 | static char *x_sockets[] = {
|
|---|
| 200 | X_UNIX_PATH "%u",
|
|---|
| 201 | "/var/X/.X11-unix/X" "%u",
|
|---|
| 202 | "/usr/spool/sockets/X11/" "%u",
|
|---|
| 203 | NULL
|
|---|
| 204 | };
|
|---|
| 205 |
|
|---|
| 206 | /*
|
|---|
| 207 | * Dito for stream pipes.
|
|---|
| 208 | */
|
|---|
| 209 |
|
|---|
| 210 | #ifdef MAY_HAVE_X11_PIPES
|
|---|
| 211 | static char *x_pipes[] = {
|
|---|
| 212 | X_PIPE_PATH "%u",
|
|---|
| 213 | "/var/X/.X11-pipe/X" "%u",
|
|---|
| 214 | NULL
|
|---|
| 215 | };
|
|---|
| 216 | #endif
|
|---|
| 217 |
|
|---|
| 218 | /*
|
|---|
| 219 | * Create the directory corresponding to dirname of `path' or fail.
|
|---|
| 220 | */
|
|---|
| 221 |
|
|---|
| 222 | static void
|
|---|
| 223 | try_mkdir (const char *path)
|
|---|
| 224 | {
|
|---|
| 225 | char *dir;
|
|---|
| 226 | char *p;
|
|---|
| 227 | int oldmask;
|
|---|
| 228 |
|
|---|
| 229 | if((dir = strdup (path)) == NULL)
|
|---|
| 230 | errx (1, "strdup: out of memory");
|
|---|
| 231 | p = strrchr (dir, '/');
|
|---|
| 232 | if (p)
|
|---|
| 233 | *p = '\0';
|
|---|
| 234 |
|
|---|
| 235 | oldmask = umask(0);
|
|---|
| 236 | mkdir (dir, 01777);
|
|---|
| 237 | umask (oldmask);
|
|---|
| 238 | free (dir);
|
|---|
| 239 | }
|
|---|
| 240 |
|
|---|
| 241 | /*
|
|---|
| 242 | * Allocate a display, returning the number of sockets in `number' and
|
|---|
| 243 | * all the corresponding sockets in `sockets'. If `tcp_socket' is
|
|---|
| 244 | * true, also allcoaet a TCP socket.
|
|---|
| 245 | *
|
|---|
| 246 | * The return value is the display allocated or -1 if an error occurred.
|
|---|
| 247 | */
|
|---|
| 248 |
|
|---|
| 249 | int
|
|---|
| 250 | get_xsockets (int *number, struct x_socket **sockets, int tcp_socket)
|
|---|
| 251 | {
|
|---|
| 252 | int dpy;
|
|---|
| 253 | struct x_socket *s;
|
|---|
| 254 | int n;
|
|---|
| 255 | int i;
|
|---|
| 256 |
|
|---|
| 257 | s = malloc (sizeof(*s) * 5);
|
|---|
| 258 | if (s == NULL)
|
|---|
| 259 | errx (1, "malloc: out of memory");
|
|---|
| 260 |
|
|---|
| 261 | try_mkdir (X_UNIX_PATH);
|
|---|
| 262 | try_mkdir (X_PIPE_PATH);
|
|---|
| 263 |
|
|---|
| 264 | for(dpy = 4; dpy < 256; ++dpy) {
|
|---|
| 265 | char **path;
|
|---|
| 266 | int tmp = 0;
|
|---|
| 267 |
|
|---|
| 268 | n = 0;
|
|---|
| 269 | for (path = x_sockets; *path; ++path) {
|
|---|
| 270 | tmp = try_socket (&s[n], dpy, *path);
|
|---|
| 271 | if (tmp == -1) {
|
|---|
| 272 | if (errno != ENOTDIR && errno != ENOENT)
|
|---|
| 273 | err(1, "failed to open '%s'", *path);
|
|---|
| 274 | } else if (tmp == 1) {
|
|---|
| 275 | while(--n >= 0) {
|
|---|
| 276 | close (s[n].fd);
|
|---|
| 277 | free (s[n].pathname);
|
|---|
| 278 | }
|
|---|
| 279 | break;
|
|---|
| 280 | } else if (tmp == 0)
|
|---|
| 281 | ++n;
|
|---|
| 282 | }
|
|---|
| 283 | if (tmp == 1)
|
|---|
| 284 | continue;
|
|---|
| 285 |
|
|---|
| 286 | #ifdef MAY_HAVE_X11_PIPES
|
|---|
| 287 | for (path = x_pipes; *path; ++path) {
|
|---|
| 288 | tmp = try_pipe (&s[n], dpy, *path);
|
|---|
| 289 | if (tmp == -1) {
|
|---|
| 290 | if (errno != ENOTDIR && errno != ENOENT && errno != ENOSYS)
|
|---|
| 291 | err(1, "failed to open '%s'", *path);
|
|---|
| 292 | } else if (tmp == 1) {
|
|---|
| 293 | while (--n >= 0) {
|
|---|
| 294 | close (s[n].fd);
|
|---|
| 295 | free (s[n].pathname);
|
|---|
| 296 | }
|
|---|
| 297 | break;
|
|---|
| 298 | } else if (tmp == 0)
|
|---|
| 299 | ++n;
|
|---|
| 300 | }
|
|---|
| 301 |
|
|---|
| 302 | if (tmp == 1)
|
|---|
| 303 | continue;
|
|---|
| 304 | #endif
|
|---|
| 305 |
|
|---|
| 306 | if (tcp_socket) {
|
|---|
| 307 | tmp = try_tcp (&s[n], dpy);
|
|---|
| 308 | if (tmp == -1)
|
|---|
| 309 | err(1, "failed to open tcp stocket");
|
|---|
| 310 | else if (tmp == 1) {
|
|---|
| 311 | while (--n >= 0) {
|
|---|
| 312 | close (s[n].fd);
|
|---|
| 313 | free (s[n].pathname);
|
|---|
| 314 | }
|
|---|
| 315 | break;
|
|---|
| 316 | } else if (tmp == 0)
|
|---|
| 317 | ++n;
|
|---|
| 318 | }
|
|---|
| 319 | break;
|
|---|
| 320 | }
|
|---|
| 321 | if (dpy == 256)
|
|---|
| 322 | errx (1, "no free x-servers");
|
|---|
| 323 | for (i = 0; i < n; ++i)
|
|---|
| 324 | if (s[i].flags & LISTENP
|
|---|
| 325 | && listen (s[i].fd, SOMAXCONN) < 0)
|
|---|
| 326 | err (1, "listen %s", s[i].pathname ? s[i].pathname : "tcp");
|
|---|
| 327 | *number = n;
|
|---|
| 328 | *sockets = s;
|
|---|
| 329 | return dpy;
|
|---|
| 330 | }
|
|---|
| 331 |
|
|---|
| 332 | /*
|
|---|
| 333 | * Change owner on the `n' sockets in `sockets' to `uid', `gid'.
|
|---|
| 334 | * Return 0 is succesful or -1 if an error occurred.
|
|---|
| 335 | */
|
|---|
| 336 |
|
|---|
| 337 | int
|
|---|
| 338 | chown_xsockets (int n, struct x_socket *sockets, uid_t uid, gid_t gid)
|
|---|
| 339 | {
|
|---|
| 340 | int i;
|
|---|
| 341 |
|
|---|
| 342 | for (i = 0; i < n; ++i)
|
|---|
| 343 | if (sockets[i].pathname != NULL)
|
|---|
| 344 | if (chown (sockets[i].pathname, uid, gid) < 0)
|
|---|
| 345 | return -1;
|
|---|
| 346 | return 0;
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | /*
|
|---|
| 350 | * Connect to local display `dnr' with local transport or TCP.
|
|---|
| 351 | * Return a file descriptor.
|
|---|
| 352 | */
|
|---|
| 353 |
|
|---|
| 354 | int
|
|---|
| 355 | connect_local_xsocket (unsigned dnr)
|
|---|
| 356 | {
|
|---|
| 357 | int fd;
|
|---|
| 358 | char **path;
|
|---|
| 359 |
|
|---|
| 360 | for (path = x_sockets; *path; ++path) {
|
|---|
| 361 | struct sockaddr_un addr;
|
|---|
| 362 |
|
|---|
| 363 | fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|---|
| 364 | if (fd < 0)
|
|---|
| 365 | break;
|
|---|
| 366 | memset (&addr, 0, sizeof(addr));
|
|---|
| 367 | addr.sun_family = AF_UNIX;
|
|---|
| 368 | snprintf (addr.sun_path, sizeof(addr.sun_path), *path, dnr);
|
|---|
| 369 | if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
|
|---|
| 370 | return fd;
|
|---|
| 371 | close(fd);
|
|---|
| 372 | }
|
|---|
| 373 | {
|
|---|
| 374 | struct sockaddr_in addr;
|
|---|
| 375 |
|
|---|
| 376 | fd = socket(AF_INET, SOCK_STREAM, 0);
|
|---|
| 377 | if (fd < 0)
|
|---|
| 378 | err (1, "socket AF_INET");
|
|---|
| 379 | memset (&addr, 0, sizeof(addr));
|
|---|
| 380 | addr.sin_family = AF_INET;
|
|---|
| 381 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|---|
| 382 | addr.sin_port = htons(6000 + dnr);
|
|---|
| 383 | if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
|
|---|
| 384 | return fd;
|
|---|
| 385 | close(fd);
|
|---|
| 386 | }
|
|---|
| 387 | err (1, "connecting to local display %u", dnr);
|
|---|
| 388 | }
|
|---|
| 389 |
|
|---|
| 390 | /*
|
|---|
| 391 | * Create a cookie file with a random cookie for the localhost. The
|
|---|
| 392 | * file name will be stored in `xauthfile' (but not larger than
|
|---|
| 393 | * `xauthfile_size'), and the cookie returned in `cookie', `cookie_sz'.
|
|---|
| 394 | * Return 0 if succesful, or errno.
|
|---|
| 395 | */
|
|---|
| 396 |
|
|---|
| 397 | int
|
|---|
| 398 | create_and_write_cookie (char *file,
|
|---|
| 399 | size_t file_size,
|
|---|
| 400 | u_char *cookie_buf,
|
|---|
| 401 | size_t cookie_sz)
|
|---|
| 402 | {
|
|---|
| 403 | Xauth auth;
|
|---|
| 404 | char tmp[64];
|
|---|
| 405 | int fd;
|
|---|
| 406 | FILE *f;
|
|---|
| 407 | char hostname[MaxHostNameLen];
|
|---|
| 408 | int saved_errno;
|
|---|
| 409 |
|
|---|
| 410 | gethostname (hostname, sizeof(hostname));
|
|---|
| 411 |
|
|---|
| 412 | auth.family = FamilyLocal;
|
|---|
| 413 | auth.address = hostname;
|
|---|
| 414 | auth.address_length = strlen(auth.address);
|
|---|
| 415 | snprintf (tmp, sizeof(tmp), "%d", display_num);
|
|---|
| 416 | auth.number_length = strlen(tmp);
|
|---|
| 417 | auth.number = tmp;
|
|---|
| 418 | auth.name = COOKIE_TYPE;
|
|---|
| 419 | auth.name_length = strlen(auth.name);
|
|---|
| 420 | auth.data_length = cookie_sz;
|
|---|
| 421 | auth.data = (char*)cookie_buf;
|
|---|
| 422 | #ifdef KRB5
|
|---|
| 423 | krb5_generate_random_block (cookie_buf, cookie_sz);
|
|---|
| 424 | #else
|
|---|
| 425 | krb_generate_random_block (cookie_buf, cookie_sz);
|
|---|
| 426 | #endif
|
|---|
| 427 |
|
|---|
| 428 | strlcpy(file, "/tmp/AXXXXXX", file_size);
|
|---|
| 429 | fd = mkstemp(file);
|
|---|
| 430 | if(fd < 0) {
|
|---|
| 431 | saved_errno = errno;
|
|---|
| 432 | syslog(LOG_ERR, "create_and_write_cookie: mkstemp: %m");
|
|---|
| 433 | return saved_errno;
|
|---|
| 434 | }
|
|---|
| 435 | f = fdopen(fd, "r+");
|
|---|
| 436 | if(f == NULL){
|
|---|
| 437 | saved_errno = errno;
|
|---|
| 438 | close(fd);
|
|---|
| 439 | return errno;
|
|---|
| 440 | }
|
|---|
| 441 | if(XauWriteAuth(f, &auth) == 0) {
|
|---|
| 442 | saved_errno = errno;
|
|---|
| 443 | fclose(f);
|
|---|
| 444 | return saved_errno;
|
|---|
| 445 | }
|
|---|
| 446 |
|
|---|
| 447 | /*
|
|---|
| 448 | * I would like to write a cookie for localhost:n here, but some
|
|---|
| 449 | * stupid code in libX11 will not look for cookies of that type,
|
|---|
| 450 | * so we are forced to use FamilyWild instead.
|
|---|
| 451 | */
|
|---|
| 452 |
|
|---|
| 453 | auth.family = FamilyWild;
|
|---|
| 454 | auth.address_length = 0;
|
|---|
| 455 |
|
|---|
| 456 | if (XauWriteAuth(f, &auth) == 0) {
|
|---|
| 457 | saved_errno = errno;
|
|---|
| 458 | fclose (f);
|
|---|
| 459 | return saved_errno;
|
|---|
| 460 | }
|
|---|
| 461 |
|
|---|
| 462 | if(fclose(f))
|
|---|
| 463 | return errno;
|
|---|
| 464 | return 0;
|
|---|
| 465 | }
|
|---|
| 466 |
|
|---|
| 467 | /*
|
|---|
| 468 | * Verify and remove cookies. Read and parse a X-connection from
|
|---|
| 469 | * `fd'. Check the cookie used is the same as in `cookie'. Remove the
|
|---|
| 470 | * cookie and copy the rest of it to `sock'.
|
|---|
| 471 | * Expect cookies iff cookiesp.
|
|---|
| 472 | * Return 0 iff ok.
|
|---|
| 473 | *
|
|---|
| 474 | * The protocol is as follows:
|
|---|
| 475 | *
|
|---|
| 476 | * C->S: [Bl] 1
|
|---|
| 477 | * unused 1
|
|---|
| 478 | * protocol major version 2
|
|---|
| 479 | * protocol minor version 2
|
|---|
| 480 | * length of auth protocol name(n) 2
|
|---|
| 481 | * length of auth protocol data 2
|
|---|
| 482 | * unused 2
|
|---|
| 483 | * authorization protocol name n
|
|---|
| 484 | * pad pad(n)
|
|---|
| 485 | * authorization protocol data d
|
|---|
| 486 | * pad pad(d)
|
|---|
| 487 | *
|
|---|
| 488 | * S->C: Failed
|
|---|
| 489 | * 0 1
|
|---|
| 490 | * length of reason 1
|
|---|
| 491 | * protocol major version 2
|
|---|
| 492 | * protocol minor version 2
|
|---|
| 493 | * length in 4 bytes unit of
|
|---|
| 494 | * additional data (n+p)/4 2
|
|---|
| 495 | * reason n
|
|---|
| 496 | * unused p = pad(n)
|
|---|
| 497 | */
|
|---|
| 498 |
|
|---|
| 499 | int
|
|---|
| 500 | verify_and_remove_cookies (int fd, int sock, int cookiesp)
|
|---|
| 501 | {
|
|---|
| 502 | u_char beg[12];
|
|---|
| 503 | int bigendianp;
|
|---|
| 504 | unsigned n, d, npad, dpad;
|
|---|
| 505 | char *protocol_name, *protocol_data;
|
|---|
| 506 | u_char zeros[6] = {0, 0, 0, 0, 0, 0};
|
|---|
| 507 | u_char refused[20] = {0, 10,
|
|---|
| 508 | 0, 0, /* protocol major version */
|
|---|
| 509 | 0, 0, /* protocol minor version */
|
|---|
| 510 | 0, 0, /* length of additional data / 4 */
|
|---|
| 511 | 'b', 'a', 'd', ' ', 'c', 'o', 'o', 'k', 'i', 'e',
|
|---|
| 512 | 0, 0};
|
|---|
| 513 |
|
|---|
| 514 | if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
|
|---|
| 515 | return 1;
|
|---|
| 516 | if (net_write (sock, beg, 6) != 6)
|
|---|
| 517 | return 1;
|
|---|
| 518 | bigendianp = beg[0] == 'B';
|
|---|
| 519 | if (bigendianp) {
|
|---|
| 520 | n = (beg[6] << 8) | beg[7];
|
|---|
| 521 | d = (beg[8] << 8) | beg[9];
|
|---|
| 522 | } else {
|
|---|
| 523 | n = (beg[7] << 8) | beg[6];
|
|---|
| 524 | d = (beg[9] << 8) | beg[8];
|
|---|
| 525 | }
|
|---|
| 526 | npad = (4 - (n % 4)) % 4;
|
|---|
| 527 | dpad = (4 - (d % 4)) % 4;
|
|---|
| 528 | protocol_name = malloc(n + npad);
|
|---|
| 529 | if (n + npad != 0 && protocol_name == NULL)
|
|---|
| 530 | return 1;
|
|---|
| 531 | protocol_data = malloc(d + dpad);
|
|---|
| 532 | if (d + dpad != 0 && protocol_data == NULL) {
|
|---|
| 533 | free (protocol_name);
|
|---|
| 534 | return 1;
|
|---|
| 535 | }
|
|---|
| 536 | if (net_read (fd, protocol_name, n + npad) != n + npad)
|
|---|
| 537 | goto fail;
|
|---|
| 538 | if (net_read (fd, protocol_data, d + dpad) != d + dpad)
|
|---|
| 539 | goto fail;
|
|---|
| 540 | if (cookiesp) {
|
|---|
| 541 | if (strncmp (protocol_name, COOKIE_TYPE, strlen(COOKIE_TYPE)) != 0)
|
|---|
| 542 | goto refused;
|
|---|
| 543 | if (d != cookie_len ||
|
|---|
| 544 | memcmp (protocol_data, cookie, cookie_len) != 0)
|
|---|
| 545 | goto refused;
|
|---|
| 546 | }
|
|---|
| 547 | free (protocol_name);
|
|---|
| 548 | free (protocol_data);
|
|---|
| 549 | if (net_write (sock, zeros, 6) != 6)
|
|---|
| 550 | return 1;
|
|---|
| 551 | return 0;
|
|---|
| 552 | refused:
|
|---|
| 553 | refused[2] = beg[2];
|
|---|
| 554 | refused[3] = beg[3];
|
|---|
| 555 | refused[4] = beg[4];
|
|---|
| 556 | refused[5] = beg[5];
|
|---|
| 557 | if (bigendianp)
|
|---|
| 558 | refused[7] = 3;
|
|---|
| 559 | else
|
|---|
| 560 | refused[6] = 3;
|
|---|
| 561 |
|
|---|
| 562 | net_write (fd, refused, sizeof(refused));
|
|---|
| 563 | fail:
|
|---|
| 564 | free (protocol_name);
|
|---|
| 565 | free (protocol_data);
|
|---|
| 566 | return 1;
|
|---|
| 567 | }
|
|---|
| 568 |
|
|---|
| 569 | /*
|
|---|
| 570 | * Return 0 iff `cookie' is compatible with the cookie for the
|
|---|
| 571 | * localhost with name given in `ai' (or `hostname') and display
|
|---|
| 572 | * number in `disp_nr'.
|
|---|
| 573 | */
|
|---|
| 574 |
|
|---|
| 575 | static int
|
|---|
| 576 | match_local_auth (Xauth* auth,
|
|---|
| 577 | struct addrinfo *ai, const char *hostname, int disp_nr)
|
|---|
| 578 | {
|
|---|
| 579 | int auth_disp;
|
|---|
| 580 | char *tmp_disp;
|
|---|
| 581 | struct addrinfo *a;
|
|---|
| 582 |
|
|---|
| 583 | tmp_disp = malloc(auth->number_length + 1);
|
|---|
| 584 | if (tmp_disp == NULL)
|
|---|
| 585 | return -1;
|
|---|
| 586 | memcpy(tmp_disp, auth->number, auth->number_length);
|
|---|
| 587 | tmp_disp[auth->number_length] = '\0';
|
|---|
| 588 | auth_disp = atoi(tmp_disp);
|
|---|
| 589 | free (tmp_disp);
|
|---|
| 590 | if (auth_disp != disp_nr)
|
|---|
| 591 | return 1;
|
|---|
| 592 | for (a = ai; a != NULL; a = a->ai_next) {
|
|---|
| 593 | if ((auth->family == FamilyLocal
|
|---|
| 594 | || auth->family == FamilyWild)
|
|---|
| 595 | && a->ai_canonname != NULL
|
|---|
| 596 | && strncmp (auth->address,
|
|---|
| 597 | a->ai_canonname,
|
|---|
| 598 | auth->address_length) == 0)
|
|---|
| 599 | return 0;
|
|---|
| 600 | }
|
|---|
| 601 | if (hostname != NULL
|
|---|
| 602 | && (auth->family == FamilyLocal
|
|---|
| 603 | || auth->family == FamilyWild)
|
|---|
| 604 | && strncmp (auth->address, hostname, auth->address_length) == 0)
|
|---|
| 605 | return 0;
|
|---|
| 606 | return 1;
|
|---|
| 607 | }
|
|---|
| 608 |
|
|---|
| 609 | /*
|
|---|
| 610 | * Find `our' cookie from the cookie file `f' and return it or NULL.
|
|---|
| 611 | */
|
|---|
| 612 |
|
|---|
| 613 | static Xauth*
|
|---|
| 614 | find_auth_cookie (FILE *f)
|
|---|
| 615 | {
|
|---|
| 616 | Xauth *ret = NULL;
|
|---|
| 617 | char local_hostname[MaxHostNameLen];
|
|---|
| 618 | char *display_str = getenv("DISPLAY");
|
|---|
| 619 | char d[MaxHostNameLen + 4];
|
|---|
| 620 | char *colon;
|
|---|
| 621 | struct addrinfo *ai;
|
|---|
| 622 | struct addrinfo hints;
|
|---|
| 623 | int disp;
|
|---|
| 624 | int error;
|
|---|
| 625 |
|
|---|
| 626 | if(display_str == NULL)
|
|---|
| 627 | display_str = ":0";
|
|---|
| 628 | strlcpy(d, display_str, sizeof(d));
|
|---|
| 629 | display_str = d;
|
|---|
| 630 | colon = strchr (display_str, ':');
|
|---|
| 631 | if (colon == NULL)
|
|---|
| 632 | disp = 0;
|
|---|
| 633 | else {
|
|---|
| 634 | *colon = '\0';
|
|---|
| 635 | disp = atoi (colon + 1);
|
|---|
| 636 | }
|
|---|
| 637 | if (strcmp (display_str, "") == 0
|
|---|
| 638 | || strncmp (display_str, "unix", 4) == 0
|
|---|
| 639 | || strncmp (display_str, "localhost", 9) == 0) {
|
|---|
| 640 | gethostname (local_hostname, sizeof(local_hostname));
|
|---|
| 641 | display_str = local_hostname;
|
|---|
| 642 | }
|
|---|
| 643 | memset (&hints, 0, sizeof(hints));
|
|---|
| 644 | hints.ai_flags = AI_CANONNAME;
|
|---|
| 645 | hints.ai_socktype = SOCK_STREAM;
|
|---|
| 646 | hints.ai_protocol = IPPROTO_TCP;
|
|---|
| 647 |
|
|---|
| 648 | error = getaddrinfo (display_str, NULL, &hints, &ai);
|
|---|
| 649 | if (error)
|
|---|
| 650 | ai = NULL;
|
|---|
| 651 |
|
|---|
| 652 | for (; (ret = XauReadAuth (f)) != NULL; XauDisposeAuth(ret)) {
|
|---|
| 653 | if (match_local_auth (ret, ai, display_str, disp) == 0) {
|
|---|
| 654 | if (ai != NULL)
|
|---|
| 655 | freeaddrinfo (ai);
|
|---|
| 656 | return ret;
|
|---|
| 657 | }
|
|---|
| 658 | }
|
|---|
| 659 | if (ai != NULL)
|
|---|
| 660 | freeaddrinfo (ai);
|
|---|
| 661 | return NULL;
|
|---|
| 662 | }
|
|---|
| 663 |
|
|---|
| 664 | /*
|
|---|
| 665 | * Get rid of the cookie that we were sent and get the correct one
|
|---|
| 666 | * from our own cookie file instead.
|
|---|
| 667 | */
|
|---|
| 668 |
|
|---|
| 669 | int
|
|---|
| 670 | replace_cookie(int xserver, int fd, char *filename, int cookiesp) /* XXX */
|
|---|
| 671 | {
|
|---|
| 672 | u_char beg[12];
|
|---|
| 673 | int bigendianp;
|
|---|
| 674 | unsigned n, d, npad, dpad;
|
|---|
| 675 | FILE *f;
|
|---|
| 676 | u_char zeros[6] = {0, 0, 0, 0, 0, 0};
|
|---|
| 677 |
|
|---|
| 678 | if (net_read (fd, beg, sizeof(beg)) != sizeof(beg))
|
|---|
| 679 | return 1;
|
|---|
| 680 | if (net_write (xserver, beg, 6) != 6)
|
|---|
| 681 | return 1;
|
|---|
| 682 | bigendianp = beg[0] == 'B';
|
|---|
| 683 | if (bigendianp) {
|
|---|
| 684 | n = (beg[6] << 8) | beg[7];
|
|---|
| 685 | d = (beg[8] << 8) | beg[9];
|
|---|
| 686 | } else {
|
|---|
| 687 | n = (beg[7] << 8) | beg[6];
|
|---|
| 688 | d = (beg[9] << 8) | beg[8];
|
|---|
| 689 | }
|
|---|
| 690 | if (n != 0 || d != 0)
|
|---|
| 691 | return 1;
|
|---|
| 692 | f = fopen(filename, "r");
|
|---|
| 693 | if (f != NULL) {
|
|---|
| 694 | Xauth *auth = find_auth_cookie (f);
|
|---|
| 695 | u_char len[6] = {0, 0, 0, 0, 0, 0};
|
|---|
| 696 |
|
|---|
| 697 | fclose (f);
|
|---|
| 698 |
|
|---|
| 699 | if (auth != NULL) {
|
|---|
| 700 | n = auth->name_length;
|
|---|
| 701 | d = auth->data_length;
|
|---|
| 702 | } else {
|
|---|
| 703 | n = 0;
|
|---|
| 704 | d = 0;
|
|---|
| 705 | }
|
|---|
| 706 | if (bigendianp) {
|
|---|
| 707 | len[0] = n >> 8;
|
|---|
| 708 | len[1] = n & 0xFF;
|
|---|
| 709 | len[2] = d >> 8;
|
|---|
| 710 | len[3] = d & 0xFF;
|
|---|
| 711 | } else {
|
|---|
| 712 | len[0] = n & 0xFF;
|
|---|
| 713 | len[1] = n >> 8;
|
|---|
| 714 | len[2] = d & 0xFF;
|
|---|
| 715 | len[3] = d >> 8;
|
|---|
| 716 | }
|
|---|
| 717 | if (net_write (xserver, len, 6) != 6) {
|
|---|
| 718 | XauDisposeAuth(auth);
|
|---|
| 719 | return 1;
|
|---|
| 720 | }
|
|---|
| 721 | if(n != 0 && net_write (xserver, auth->name, n) != n) {
|
|---|
| 722 | XauDisposeAuth(auth);
|
|---|
| 723 | return 1;
|
|---|
| 724 | }
|
|---|
| 725 | npad = (4 - (n % 4)) % 4;
|
|---|
| 726 | if (npad && net_write (xserver, zeros, npad) != npad) {
|
|---|
| 727 | XauDisposeAuth(auth);
|
|---|
| 728 | return 1;
|
|---|
| 729 | }
|
|---|
| 730 | if (d != 0 && net_write (xserver, auth->data, d) != d) {
|
|---|
| 731 | XauDisposeAuth(auth);
|
|---|
| 732 | return 1;
|
|---|
| 733 | }
|
|---|
| 734 | XauDisposeAuth(auth);
|
|---|
| 735 | dpad = (4 - (d % 4)) % 4;
|
|---|
| 736 | if (dpad && net_write (xserver, zeros, dpad) != dpad)
|
|---|
| 737 | return 1;
|
|---|
| 738 | } else {
|
|---|
| 739 | if(net_write(xserver, zeros, 6) != 6)
|
|---|
| 740 | return 1;
|
|---|
| 741 | }
|
|---|
| 742 | return 0;
|
|---|
| 743 | }
|
|---|
| 744 |
|
|---|
| 745 | /*
|
|---|
| 746 | * Some simple controls on the address and corresponding socket
|
|---|
| 747 | */
|
|---|
| 748 |
|
|---|
| 749 | int
|
|---|
| 750 | suspicious_address (int sock, struct sockaddr *addr)
|
|---|
| 751 | {
|
|---|
| 752 | char data[40];
|
|---|
| 753 | socklen_t len = sizeof(data);
|
|---|
| 754 |
|
|---|
| 755 | switch (addr->sa_family) {
|
|---|
| 756 | case AF_INET:
|
|---|
| 757 | return ((struct sockaddr_in *)addr)->sin_addr.s_addr !=
|
|---|
| 758 | htonl(INADDR_LOOPBACK)
|
|---|
| 759 | #if defined(IP_OPTIONS) && defined(HAVE_GETSOCKOPT)
|
|---|
| 760 | || getsockopt (sock, IPPROTO_IP, IP_OPTIONS, data, &len) < 0
|
|---|
| 761 | || len != 0
|
|---|
| 762 | #endif
|
|---|
| 763 | ;
|
|---|
| 764 | break;
|
|---|
| 765 | #ifdef HAVE_IPV6
|
|---|
| 766 | case AF_INET6:
|
|---|
| 767 | /* XXX check route headers */
|
|---|
| 768 | return !IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6*)addr)->sin6_addr);
|
|---|
| 769 | #endif
|
|---|
| 770 | default:
|
|---|
| 771 | return 1;
|
|---|
| 772 | }
|
|---|
| 773 | }
|
|---|
| 774 |
|
|---|
| 775 | /*
|
|---|
| 776 | * This really sucks, but these functions are used and if we're not
|
|---|
| 777 | * linking against libkrb they don't exist. Using the heimdal storage
|
|---|
| 778 | * functions will not work either cause we do not always link with
|
|---|
| 779 | * libkrb5 either.
|
|---|
| 780 | */
|
|---|
| 781 |
|
|---|
| 782 | int
|
|---|
| 783 | kx_get_int(void *f, uint32_t *to, int size, int lsb)
|
|---|
| 784 | {
|
|---|
| 785 | int i;
|
|---|
| 786 | unsigned char *from = (unsigned char *)f;
|
|---|
| 787 |
|
|---|
| 788 | *to = 0;
|
|---|
| 789 | if(lsb){
|
|---|
| 790 | for(i = size-1; i >= 0; i--)
|
|---|
| 791 | *to = (*to << 8) | from[i];
|
|---|
| 792 | }else{
|
|---|
| 793 | for(i = 0; i < size; i++)
|
|---|
| 794 | *to = (*to << 8) | from[i];
|
|---|
| 795 | }
|
|---|
| 796 | return size;
|
|---|
| 797 | }
|
|---|
| 798 |
|
|---|
| 799 | int
|
|---|
| 800 | kx_put_int(uint32_t from, void *to, size_t rem, int size)
|
|---|
| 801 | {
|
|---|
| 802 | int i;
|
|---|
| 803 | unsigned char *p = (unsigned char *)to;
|
|---|
| 804 |
|
|---|
| 805 | if (rem < size)
|
|---|
| 806 | return -1;
|
|---|
| 807 |
|
|---|
| 808 | for(i = size - 1; i >= 0; i--){
|
|---|
| 809 | p[i] = from & 0xff;
|
|---|
| 810 | from >>= 8;
|
|---|
| 811 | }
|
|---|
| 812 | return size;
|
|---|
| 813 | }
|
|---|