fixes from Stefan Metzmacher
[heimdal.git] / appl / kx / kx.c
blobb5eb05350040a1cc6aba74da780feea93f644294
1 /*
2 * Copyright (c) 1995-2003 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 static int nchild;
39 static int donep;
42 * Signal handler that justs waits for the children when they die.
45 static RETSIGTYPE
46 childhandler (int sig)
48 pid_t pid;
49 int status;
51 do {
52 pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
53 if (pid > 0 && (WIFEXITED(status) || WIFSIGNALED(status)))
54 if (--nchild == 0 && donep)
55 exit (0);
56 } while(pid > 0);
57 signal (SIGCHLD, childhandler);
58 SIGRETURN(0);
62 * Handler for SIGUSR1.
63 * This signal means that we should wait until there are no children
64 * left and then exit.
67 static RETSIGTYPE
68 usr1handler (int sig)
70 donep = 1;
72 SIGRETURN(0);
76 * Almost the same as for SIGUSR1, except we should exit immediately
77 * if there are no active children.
80 static RETSIGTYPE
81 usr2handler (int sig)
83 donep = 1;
84 if (nchild == 0)
85 exit (0);
87 SIGRETURN(0);
91 * Establish authenticated connection. Return socket or -1.
94 static int
95 connect_host (kx_context *kc)
97 struct addrinfo *ai, *a;
98 struct addrinfo hints;
99 int error;
100 char portstr[NI_MAXSERV];
101 socklen_t addrlen;
102 int s = -1;
103 struct sockaddr_storage thisaddr_ss;
104 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
106 memset (&hints, 0, sizeof(hints));
107 hints.ai_socktype = SOCK_STREAM;
108 hints.ai_protocol = IPPROTO_TCP;
110 snprintf (portstr, sizeof(portstr), "%u", ntohs(kc->port));
112 error = getaddrinfo (kc->host, portstr, &hints, &ai);
113 if (error) {
114 warnx ("%s: %s", kc->host, gai_strerror(error));
115 return -1;
118 for (a = ai; a != NULL; a = a->ai_next) {
119 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
120 if (s < 0)
121 continue;
122 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
123 warn ("connect(%s)", kc->host);
124 close (s);
125 continue;
127 break;
130 if (a == NULL) {
131 freeaddrinfo (ai);
132 return -1;
135 addrlen = sizeof(thisaddr_ss);
136 if (getsockname (s, thisaddr, &addrlen) < 0 ||
137 addrlen != a->ai_addrlen)
138 err(1, "getsockname(%s)", kc->host);
139 memcpy (&kc->__ss_this, thisaddr, sizeof(kc->__ss_this));
140 kc->thisaddr_len = addrlen;
141 memcpy (&kc->__ss_that, a->ai_addr, sizeof(kc->__ss_that));
142 kc->thataddr_len = a->ai_addrlen;
143 freeaddrinfo (ai);
144 if ((*kc->authenticate)(kc, s))
145 return -1;
146 return s;
150 * Get rid of the cookie that we were sent and get the correct one
151 * from our own cookie file instead and then just copy data in both
152 * directions.
155 static int
156 passive_session (int xserver, int fd, kx_context *kc)
158 if (replace_cookie (xserver, fd, XauFileName(), 1))
159 return 1;
160 else
161 return copy_encrypted (kc, xserver, fd);
164 static int
165 active_session (int xserver, int fd, kx_context *kc)
167 if (verify_and_remove_cookies (xserver, fd, 1))
168 return 1;
169 else
170 return copy_encrypted (kc, xserver, fd);
174 * fork (unless debugp) and print the output that will be used by the
175 * script to capture the display, xauth cookie and pid.
178 static void
179 status_output (int debugp)
181 if(debugp)
182 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display, xauthfile);
183 else {
184 pid_t pid;
186 pid = fork();
187 if (pid < 0) {
188 err(1, "fork");
189 } else if (pid > 0) {
190 printf ("%u\t%s\t%s\n", (unsigned)pid, display, xauthfile);
191 exit (0);
192 } else {
193 fclose(stdout);
199 * Obtain an authenticated connection on `kc'. Send a kx message
200 * saying we are `kc->user' and want to use passive mode. Wait for
201 * answer on that connection and fork of a child for every new
202 * connection we have to make.
205 static int
206 doit_passive (kx_context *kc)
208 int otherside;
209 u_char msg[1024], *p;
210 int len;
211 uint32_t tmp;
212 const char *host = kc->host;
214 otherside = connect_host (kc);
216 if (otherside < 0)
217 return 1;
218 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
219 if (kc->keepalive_flag) {
220 int one = 1;
222 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
223 sizeof(one));
225 #endif
227 p = msg;
228 *p++ = INIT;
229 len = strlen(kc->user);
230 p += kx_put_int (len, p, sizeof(msg) - 1, 4);
231 memcpy(p, kc->user, len);
232 p += len;
233 *p++ = PASSIVE | (kc->keepalive_flag ? KEEP_ALIVE : 0);
234 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
235 err (1, "write to %s", host);
236 len = kx_read (kc, otherside, msg, sizeof(msg));
237 if (len <= 0)
238 errx (1,
239 "error reading initial message from %s: "
240 "this probably means it's using an old version.",
241 host);
242 p = (u_char *)msg;
243 if (*p == ERROR) {
244 p++;
245 p += kx_get_int (p, &tmp, 4, 0);
246 errx (1, "%s: %.*s", host, (int)tmp, p);
247 } else if (*p != ACK) {
248 errx (1, "%s: strange msg %d", host, *p);
249 } else
250 p++;
251 p += kx_get_int (p, &tmp, 4, 0);
252 memcpy(display, p, tmp);
253 display[tmp] = '\0';
254 p += tmp;
256 p += kx_get_int (p, &tmp, 4, 0);
257 memcpy(xauthfile, p, tmp);
258 xauthfile[tmp] = '\0';
259 p += tmp;
261 status_output (kc->debug_flag);
262 for (;;) {
263 pid_t child;
265 len = kx_read (kc, otherside, msg, sizeof(msg));
266 if (len < 0)
267 err (1, "read from %s", host);
268 else if (len == 0)
269 return 0;
271 p = (u_char *)msg;
272 if (*p == ERROR) {
273 p++;
274 p += kx_get_int (p, &tmp, 4, 0);
275 errx (1, "%s: %.*s", host, (int)tmp, p);
276 } else if(*p != NEW_CONN) {
277 errx (1, "%s: strange msg %d", host, *p);
278 } else {
279 p++;
280 p += kx_get_int (p, &tmp, 4, 0);
283 ++nchild;
284 child = fork ();
285 if (child < 0) {
286 warn("fork");
287 continue;
288 } else if (child == 0) {
289 int fd;
290 int xserver;
292 close (otherside);
294 socket_set_port(kc->thataddr, htons(tmp));
296 fd = socket (kc->thataddr->sa_family, SOCK_STREAM, 0);
297 if (fd < 0)
298 err(1, "socket");
299 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
301 int one = 1;
303 setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
304 sizeof(one));
306 #endif
307 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
308 if (kc->keepalive_flag) {
309 int one = 1;
311 setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
312 sizeof(one));
314 #endif
316 if (connect (fd, kc->thataddr, kc->thataddr_len) < 0)
317 err(1, "connect(%s)", host);
319 int d = 0;
320 char *s;
322 s = getenv ("DISPLAY");
323 if (s != NULL) {
324 s = strchr (s, ':');
325 if (s != NULL)
326 d = atoi (s + 1);
329 xserver = connect_local_xsocket (d);
330 if (xserver < 0)
331 return 1;
333 return passive_session (xserver, fd, kc);
334 } else {
340 * Allocate a local pseudo-xserver and wait for connections
343 static int
344 doit_active (kx_context *kc)
346 int otherside;
347 int nsockets;
348 struct x_socket *sockets;
349 u_char msg[1024], *p;
350 int len = strlen(kc->user);
351 int tmp, tmp2;
352 char *str;
353 int i;
354 size_t rem;
355 uint32_t other_port;
356 int error;
357 const char *host = kc->host;
359 otherside = connect_host (kc);
360 if (otherside < 0)
361 return 1;
362 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
363 if (kc->keepalive_flag) {
364 int one = 1;
366 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
367 sizeof(one));
369 #endif
370 p = msg;
371 rem = sizeof(msg);
372 *p++ = INIT;
373 --rem;
374 len = strlen(kc->user);
375 tmp = kx_put_int (len, p, rem, 4);
376 if (tmp < 0)
377 return 1;
378 p += tmp;
379 rem -= tmp;
380 memcpy(p, kc->user, len);
381 p += len;
382 rem -= len;
383 *p++ = (kc->keepalive_flag ? KEEP_ALIVE : 0);
384 --rem;
386 str = getenv("DISPLAY");
387 if (str == NULL || (str = strchr(str, ':')) == NULL)
388 str = ":0";
389 len = strlen (str);
390 tmp = kx_put_int (len, p, rem, 4);
391 if (tmp < 0)
392 return 1;
393 rem -= tmp;
394 p += tmp;
395 memcpy (p, str, len);
396 p += len;
397 rem -= len;
399 str = getenv("XAUTHORITY");
400 if (str == NULL)
401 str = "";
402 len = strlen (str);
403 tmp = kx_put_int (len, p, rem, 4);
404 if (tmp < 0)
405 return 1;
406 p += len;
407 rem -= len;
408 memcpy (p, str, len);
409 p += len;
410 rem -= len;
412 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
413 err (1, "write to %s", host);
415 len = kx_read (kc, otherside, msg, sizeof(msg));
416 if (len < 0)
417 err (1, "read from %s", host);
418 p = (u_char *)msg;
419 if (*p == ERROR) {
420 uint32_t u32;
422 p++;
423 p += kx_get_int (p, &u32, 4, 0);
424 errx (1, "%s: %.*s", host, (int)u32, p);
425 } else if (*p != ACK) {
426 errx (1, "%s: strange msg %d", host, *p);
427 } else
428 p++;
430 tmp2 = get_xsockets (&nsockets, &sockets, kc->tcp_flag);
431 if (tmp2 < 0)
432 return 1;
433 display_num = tmp2;
434 if (kc->tcp_flag)
435 snprintf (display, display_size, "localhost:%u", display_num);
436 else
437 snprintf (display, display_size, ":%u", display_num);
438 error = create_and_write_cookie (xauthfile, xauthfile_size,
439 cookie, cookie_len);
440 if (error) {
441 warnx ("failed creating cookie file: %s", strerror(error));
442 return 1;
444 status_output (kc->debug_flag);
445 for (;;) {
446 fd_set fdset;
447 pid_t child;
448 int fd, thisfd = -1;
449 socklen_t zero = 0;
451 FD_ZERO(&fdset);
452 for (i = 0; i < nsockets; ++i) {
453 if (sockets[i].fd >= FD_SETSIZE)
454 errx (1, "fd too large");
455 FD_SET(sockets[i].fd, &fdset);
457 if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0)
458 continue;
459 for (i = 0; i < nsockets; ++i)
460 if (FD_ISSET(sockets[i].fd, &fdset)) {
461 thisfd = sockets[i].fd;
462 break;
464 fd = accept (thisfd, NULL, &zero);
465 if (fd < 0) {
466 if (errno == EINTR)
467 continue;
468 else
469 err(1, "accept");
472 p = msg;
473 *p++ = NEW_CONN;
474 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
475 err (1, "write to %s", host);
476 len = kx_read (kc, otherside, msg, sizeof(msg));
477 if (len < 0)
478 err (1, "read from %s", host);
479 p = (u_char *)msg;
480 if (*p == ERROR) {
481 uint32_t val;
483 p++;
484 p += kx_get_int (p, &val, 4, 0);
485 errx (1, "%s: %.*s", host, (int)val, p);
486 } else if (*p != NEW_CONN) {
487 errx (1, "%s: strange msg %d", host, *p);
488 } else {
489 p++;
490 p += kx_get_int (p, &other_port, 4, 0);
493 ++nchild;
494 child = fork ();
495 if (child < 0) {
496 warn("fork");
497 continue;
498 } else if (child == 0) {
499 int s;
501 for (i = 0; i < nsockets; ++i)
502 close (sockets[i].fd);
504 close (otherside);
506 socket_set_port(kc->thataddr, htons(tmp));
508 s = socket (kc->thataddr->sa_family, SOCK_STREAM, 0);
509 if (s < 0)
510 err(1, "socket");
511 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
513 int one = 1;
515 setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
516 sizeof(one));
518 #endif
519 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
520 if (kc->keepalive_flag) {
521 int one = 1;
523 setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
524 sizeof(one));
526 #endif
528 if (connect (s, kc->thataddr, kc->thataddr_len) < 0)
529 err(1, "connect");
531 return active_session (fd, s, kc);
532 } else {
533 close (fd);
539 * Should we interpret `disp' as this being a passive call?
542 static int
543 check_for_passive (const char *disp)
545 char local_hostname[MaxHostNameLen];
547 gethostname (local_hostname, sizeof(local_hostname));
549 return disp != NULL &&
550 (*disp == ':'
551 || strncmp(disp, "unix", 4) == 0
552 || strncmp(disp, "localhost", 9) == 0
553 || strncmp(disp, local_hostname, strlen(local_hostname)) == 0);
557 * Set up signal handlers and then call the functions.
560 static int
561 doit (kx_context *kc, int passive_flag)
563 signal (SIGCHLD, childhandler);
564 signal (SIGUSR1, usr1handler);
565 signal (SIGUSR2, usr2handler);
566 if (passive_flag)
567 return doit_passive (kc);
568 else
569 return doit_active (kc);
572 #ifdef KRB4
575 * Start a v4-authenticatated kx connection.
578 static int
579 doit_v4 (const char *host, int port, const char *user,
580 int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag)
582 int ret;
583 kx_context context;
585 krb4_make_context (&context);
586 context_set (&context,
587 host, user, port, debug_flag, keepalive_flag, tcp_flag);
589 ret = doit (&context, passive_flag);
590 context_destroy (&context);
591 return ret;
593 #endif /* KRB4 */
595 #ifdef KRB5
598 * Start a v5-authenticatated kx connection.
601 static int
602 doit_v5 (const char *host, int port, const char *user,
603 int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag)
605 int ret;
606 kx_context context;
608 krb5_make_context (&context);
609 context_set (&context,
610 host, user, port, debug_flag, keepalive_flag, tcp_flag);
612 ret = doit (&context, passive_flag);
613 context_destroy (&context);
614 return ret;
616 #endif /* KRB5 */
619 * Variables set from the arguments
622 #ifdef KRB4
623 static int use_v4 = -1;
624 #ifdef HAVE_KRB_ENABLE_DEBUG
625 static int krb_debug_flag = 0;
626 #endif /* HAVE_KRB_ENABLE_DEBUG */
627 #endif /* KRB4 */
628 #ifdef KRB5
629 static int use_v5 = -1;
630 #endif
631 static char *port_str = NULL;
632 static const char *user = NULL;
633 static int tcp_flag = 0;
634 static int passive_flag = 0;
635 static int keepalive_flag = 1;
636 static int debug_flag = 0;
637 static int version_flag = 0;
638 static int help_flag = 0;
640 struct getargs args[] = {
641 #ifdef KRB4
642 { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4",
643 NULL },
644 #ifdef HAVE_KRB_ENABLE_DEBUG
645 { "krb4-debug", 'D', arg_flag, &krb_debug_flag,
646 "enable krb4 debugging" },
647 #endif /* HAVE_KRB_ENABLE_DEBUG */
648 #endif /* KRB4 */
649 #ifdef KRB5
650 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5",
651 NULL },
652 #endif
653 { "port", 'p', arg_string, &port_str, "Use this port",
654 "number-of-service" },
655 { "user", 'l', arg_string, &user, "Run as this user",
656 NULL },
657 { "tcp", 't', arg_flag, &tcp_flag,
658 "Use a TCP connection for X11" },
659 { "passive", 'P', arg_flag, &passive_flag,
660 "Force a passive connection" },
661 { "keepalive", 'k', arg_negative_flag, &keepalive_flag,
662 "disable keep-alives" },
663 { "debug", 'd', arg_flag, &debug_flag,
664 "Enable debug information" },
665 { "version", 0, arg_flag, &version_flag, "Print version",
666 NULL },
667 { "help", 0, arg_flag, &help_flag, NULL,
668 NULL }
671 static void
672 usage(int ret)
674 arg_printusage (args,
675 sizeof(args) / sizeof(args[0]),
676 NULL,
677 "host");
678 exit (ret);
682 * kx - forward an x-connection over a kerberos-encrypted channel.
686 main(int argc, char **argv)
688 int port = 0;
689 int optidx = 0;
690 int ret = 1;
691 char *host = NULL;
693 setprogname (argv[0]);
695 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
696 &optidx))
697 usage (1);
699 if (help_flag)
700 usage (0);
702 if (version_flag) {
703 print_version (NULL);
704 return 0;
707 if (optidx != argc - 1)
708 usage (1);
710 host = argv[optidx];
712 if (port_str) {
713 struct servent *s = roken_getservbyname (port_str, "tcp");
715 if (s)
716 port = s->s_port;
717 else {
718 char *ptr;
720 port = strtol (port_str, &ptr, 10);
721 if (port == 0 && ptr == port_str)
722 errx (1, "Bad port `%s'", port_str);
723 port = htons(port);
727 if (user == NULL) {
728 user = get_default_username ();
729 if (user == NULL)
730 errx (1, "who are you?");
733 if (!passive_flag)
734 passive_flag = check_for_passive (getenv("DISPLAY"));
736 #if defined(HAVE_KERNEL_ENABLE_DEBUG)
737 if (krb_debug_flag)
738 krb_enable_debug ();
739 #endif
741 #if defined(KRB4) && defined(KRB5)
742 if(use_v4 == -1 && use_v5 == 1)
743 use_v4 = 0;
744 if(use_v5 == -1 && use_v4 == 1)
745 use_v5 = 0;
746 #endif
748 #ifdef KRB5
749 if (ret && use_v5) {
750 if (port == 0)
751 port = krb5_getportbyname(NULL, "kx", "tcp", KX_PORT);
752 ret = doit_v5 (host, port, user,
753 passive_flag, debug_flag, keepalive_flag, tcp_flag);
755 #endif
756 #ifdef KRB4
757 if (ret && use_v4) {
758 if (port == 0)
759 port = k_getportbyname("kx", "tcp", htons(KX_PORT));
760 ret = doit_v4 (host, port, user,
761 passive_flag, debug_flag, keepalive_flag, tcp_flag);
763 #endif
764 return ret;