x
[heimdal.git] / appl / kx / common.c
blobc8b89007edb3f112a3ae885041ed26979da27d73
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.
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.
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.
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.
34 #include "kx.h"
36 RCSID("$Id$");
38 char x_socket[MaxPathLen];
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);
48 #ifndef X_UNIX_PATH
49 #define X_UNIX_PATH "/tmp/.X11-unix/X"
50 #endif
52 #ifndef X_PIPE_PATH
53 #define X_PIPE_PATH "/tmp/.X11-pipe/X"
54 #endif
57 * Allocate a unix domain socket in `s' for display `dpy' and with
58 * filename `pattern'
60 * 0 if all is OK
61 * -1 if bind failed badly
62 * 1 if dpy is already used */
64 static int
65 try_socket (struct x_socket *s, int dpy, const char *pattern)
67 struct sockaddr_un addr;
68 int fd;
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
86 return 1;
87 else
88 return -1;
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;
98 #ifdef MAY_HAVE_X11_PIPES
100 * Allocate a stream (masqueraded as a named pipe)
102 * 0 if all is OK
103 * -1 if bind failed badly
104 * 1 if dpy is already used
107 static int
108 try_pipe (struct x_socket *s, int dpy, const char *pattern)
110 char path[MAXPATHLEN];
111 int ret;
112 int fd;
113 int pipefd[2];
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;
124 close (fd);
126 ret = pipe (pipefd);
127 if (ret < 0)
128 err (1, "pipe");
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");
137 ret = fattach (pipefd[1], path);
138 if (ret < 0)
139 err (1, "fattach %s", path);
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;
149 #endif /* MAY_HAVE_X11_PIPES */
152 * Try to create a TCP socket in `s' corresponding to display `dpy'.
154 * 0 if all is OK
155 * -1 if bind failed badly
156 * 1 if dpy is already used
159 static int
160 try_tcp (struct x_socket *s, int dpy)
162 struct sockaddr_in tcpaddr;
163 struct in_addr local;
164 int one = 1;
165 int fd;
167 memset(&local, 0, sizeof(local));
168 local.s_addr = htonl(INADDR_LOOPBACK);
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;
189 s->fd = fd;
190 s->pathname = NULL;
191 s->flags = TCP;
192 return 0;
196 * The potential places to create unix sockets.
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
207 * Dito for stream pipes.
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
216 #endif
219 * Create the directory corresponding to dirname of `path' or fail.
222 static void
223 try_mkdir (const char *path)
225 char *dir;
226 char *p;
227 int oldmask;
229 if((dir = strdup (path)) == NULL)
230 errx (1, "strdup: out of memory");
231 p = strrchr (dir, '/');
232 if (p)
233 *p = '\0';
235 oldmask = umask(0);
236 mkdir (dir, 01777);
237 umask (oldmask);
238 free (dir);
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.
246 * The return value is the display allocated or -1 if an error occurred.
250 get_xsockets (int *number, struct x_socket **sockets, int tcp_socket)
252 int dpy;
253 struct x_socket *s;
254 int n;
255 int i;
257 s = malloc (sizeof(*s) * 5);
258 if (s == NULL)
259 errx (1, "malloc: out of memory");
261 try_mkdir (X_UNIX_PATH);
262 try_mkdir (X_PIPE_PATH);
264 for(dpy = 4; dpy < 256; ++dpy) {
265 char **path;
266 int tmp = 0;
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 return -1;
274 } else if (tmp == 1) {
275 while(--n >= 0) {
276 close (s[n].fd);
277 free (s[n].pathname);
279 break;
280 } else if (tmp == 0)
281 ++n;
283 if (tmp == 1)
284 continue;
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 return -1;
292 } else if (tmp == 1) {
293 while (--n >= 0) {
294 close (s[n].fd);
295 free (s[n].pathname);
297 break;
298 } else if (tmp == 0)
299 ++n;
302 if (tmp == 1)
303 continue;
304 #endif
306 if (tcp_socket) {
307 tmp = try_tcp (&s[n], dpy);
308 if (tmp == -1)
309 return -1;
310 else if (tmp == 1) {
311 while (--n >= 0) {
312 close (s[n].fd);
313 free (s[n].pathname);
315 break;
316 } else if (tmp == 0)
317 ++n;
319 break;
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;
333 * Change owner on the `n' sockets in `sockets' to `uid', `gid'.
334 * Return 0 is succesful or -1 if an error occurred.
338 chown_xsockets (int n, struct x_socket *sockets, uid_t uid, gid_t gid)
340 int i;
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;
350 * Connect to local display `dnr' with local transport or TCP.
351 * Return a file descriptor.
355 connect_local_xsocket (unsigned dnr)
357 int fd;
358 char **path;
360 for (path = x_sockets; *path; ++path) {
361 struct sockaddr_un addr;
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);
374 struct sockaddr_in addr;
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);
387 err (1, "connecting to local display %u", dnr);
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.
398 create_and_write_cookie (char *file,
399 size_t file_size,
400 u_char *cookie_buf,
401 size_t cookie_sz)
403 Xauth auth;
404 char tmp[64];
405 int fd;
406 FILE *f;
407 char hostname[MaxHostNameLen];
408 int saved_errno;
410 gethostname (hostname, sizeof(hostname));
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
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;
435 f = fdopen(fd, "r+");
436 if(f == NULL){
437 saved_errno = errno;
438 close(fd);
439 return errno;
441 if(XauWriteAuth(f, &auth) == 0) {
442 saved_errno = errno;
443 fclose(f);
444 return saved_errno;
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.
453 auth.family = FamilyWild;
454 auth.address_length = 0;
456 if (XauWriteAuth(f, &auth) == 0) {
457 saved_errno = errno;
458 fclose (f);
459 return saved_errno;
462 if(fclose(f))
463 return errno;
464 return 0;
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.
474 * The protocol is as follows:
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)
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)
500 verify_and_remove_cookies (int fd, int sock, int cookiesp)
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};
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];
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;
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;
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;
562 net_write (fd, refused, sizeof(refused));
563 fail:
564 free (protocol_name);
565 free (protocol_data);
566 return 1;
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'.
575 static int
576 match_local_auth (Xauth* auth,
577 struct addrinfo *ai, const char *hostname, int disp_nr)
579 int auth_disp;
580 char *tmp_disp;
581 struct addrinfo *a;
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;
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;
610 * Find `our' cookie from the cookie file `f' and return it or NULL.
613 static Xauth*
614 find_auth_cookie (FILE *f)
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;
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);
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;
643 memset (&hints, 0, sizeof(hints));
644 hints.ai_flags = AI_CANONNAME;
645 hints.ai_socktype = SOCK_STREAM;
646 hints.ai_protocol = IPPROTO_TCP;
648 error = getaddrinfo (display_str, NULL, &hints, &ai);
649 if (error)
650 ai = NULL;
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;
659 if (ai != NULL)
660 freeaddrinfo (ai);
661 return NULL;
665 * Get rid of the cookie that we were sent and get the correct one
666 * from our own cookie file instead.
670 replace_cookie(int xserver, int fd, char *filename, int cookiesp) /* XXX */
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};
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];
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};
697 fclose (f);
699 if (auth != NULL) {
700 n = auth->name_length;
701 d = auth->data_length;
702 } else {
703 n = 0;
704 d = 0;
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;
717 if (net_write (xserver, len, 6) != 6) {
718 XauDisposeAuth(auth);
719 return 1;
721 if(n != 0 && net_write (xserver, auth->name, n) != n) {
722 XauDisposeAuth(auth);
723 return 1;
725 npad = (4 - (n % 4)) % 4;
726 if (npad && net_write (xserver, zeros, npad) != npad) {
727 XauDisposeAuth(auth);
728 return 1;
730 if (d != 0 && net_write (xserver, auth->data, d) != d) {
731 XauDisposeAuth(auth);
732 return 1;
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;
742 return 0;
746 * Some simple controls on the address and corresponding socket
750 suspicious_address (int sock, struct sockaddr *addr)
752 char data[40];
753 socklen_t len = sizeof(data);
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
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;
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.
783 kx_get_int(void *f, uint32_t *to, int size, int lsb)
785 int i;
786 unsigned char *from = (unsigned char *)f;
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];
796 return size;
800 kx_put_int(uint32_t from, void *to, size_t rem, int size)
802 int i;
803 unsigned char *p = (unsigned char *)to;
805 if (rem < size)
806 return -1;
808 for(i = size - 1; i >= 0; i--){
809 p[i] = from & 0xff;
810 from >>= 8;
812 return size;