No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / kx / kx.c
blob569d182dc1097b29d04d787e87b5f2610a6dccbb
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("$Heimdal: kx.c 20452 2007-04-19 20:04:19Z lha $"
37 "$NetBSD$");
39 static int nchild;
40 static int donep;
43 * Signal handler that justs waits for the children when they die.
46 static RETSIGTYPE
47 childhandler (int sig)
49 pid_t pid;
50 int status;
52 do {
53 pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
54 if (pid > 0 && (WIFEXITED(status) || WIFSIGNALED(status)))
55 if (--nchild == 0 && donep)
56 exit (0);
57 } while(pid > 0);
58 signal (SIGCHLD, childhandler);
59 SIGRETURN(0);
63 * Handler for SIGUSR1.
64 * This signal means that we should wait until there are no children
65 * left and then exit.
68 static RETSIGTYPE
69 usr1handler (int sig)
71 donep = 1;
73 SIGRETURN(0);
77 * Almost the same as for SIGUSR1, except we should exit immediately
78 * if there are no active children.
81 static RETSIGTYPE
82 usr2handler (int sig)
84 donep = 1;
85 if (nchild == 0)
86 exit (0);
88 SIGRETURN(0);
92 * Establish authenticated connection. Return socket or -1.
95 static int
96 connect_host (kx_context *kc)
98 struct addrinfo *ai, *a;
99 struct addrinfo hints;
100 int error;
101 char portstr[NI_MAXSERV];
102 socklen_t addrlen;
103 int s = -1;
104 struct sockaddr_storage thisaddr_ss;
105 struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
107 memset (&hints, 0, sizeof(hints));
108 hints.ai_socktype = SOCK_STREAM;
109 hints.ai_protocol = IPPROTO_TCP;
111 snprintf (portstr, sizeof(portstr), "%u", ntohs(kc->port));
113 error = getaddrinfo (kc->host, portstr, &hints, &ai);
114 if (error) {
115 warnx ("%s: %s", kc->host, gai_strerror(error));
116 return -1;
119 for (a = ai; a != NULL; a = a->ai_next) {
120 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
121 if (s < 0)
122 continue;
123 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
124 warn ("connect(%s)", kc->host);
125 close (s);
126 continue;
128 break;
131 if (a == NULL) {
132 freeaddrinfo (ai);
133 return -1;
136 addrlen = sizeof(thisaddr_ss);
137 if (getsockname (s, thisaddr, &addrlen) < 0 ||
138 addrlen != a->ai_addrlen)
139 err(1, "getsockname(%s)", kc->host);
140 memcpy (&kc->__ss_this, thisaddr, sizeof(kc->__ss_this));
141 kc->thisaddr_len = addrlen;
142 memcpy (&kc->__ss_that, a->ai_addr, sizeof(kc->__ss_that));
143 kc->thataddr_len = a->ai_addrlen;
144 freeaddrinfo (ai);
145 if ((*kc->authenticate)(kc, s))
146 return -1;
147 return s;
151 * Get rid of the cookie that we were sent and get the correct one
152 * from our own cookie file instead and then just copy data in both
153 * directions.
156 static int
157 passive_session (int xserver, int fd, kx_context *kc)
159 if (replace_cookie (xserver, fd, XauFileName(), 1))
160 return 1;
161 else
162 return copy_encrypted (kc, xserver, fd);
165 static int
166 active_session (int xserver, int fd, kx_context *kc)
168 if (verify_and_remove_cookies (xserver, fd, 1))
169 return 1;
170 else
171 return copy_encrypted (kc, xserver, fd);
175 * fork (unless debugp) and print the output that will be used by the
176 * script to capture the display, xauth cookie and pid.
179 static void
180 status_output (int debugp)
182 if(debugp)
183 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display, xauthfile);
184 else {
185 pid_t pid;
187 pid = fork();
188 if (pid < 0) {
189 err(1, "fork");
190 } else if (pid > 0) {
191 printf ("%u\t%s\t%s\n", (unsigned)pid, display, xauthfile);
192 exit (0);
193 } else {
194 fclose(stdout);
200 * Obtain an authenticated connection on `kc'. Send a kx message
201 * saying we are `kc->user' and want to use passive mode. Wait for
202 * answer on that connection and fork of a child for every new
203 * connection we have to make.
206 static int
207 doit_passive (kx_context *kc)
209 int otherside;
210 u_char msg[1024], *p;
211 int len;
212 uint32_t tmp;
213 const char *host = kc->host;
215 otherside = connect_host (kc);
217 if (otherside < 0)
218 return 1;
219 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
220 if (kc->keepalive_flag) {
221 int one = 1;
223 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
224 sizeof(one));
226 #endif
228 p = msg;
229 *p++ = INIT;
230 len = strlen(kc->user);
231 p += kx_put_int (len, p, sizeof(msg) - 1, 4);
232 memcpy(p, kc->user, len);
233 p += len;
234 *p++ = PASSIVE | (kc->keepalive_flag ? KEEP_ALIVE : 0);
235 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
236 err (1, "write to %s", host);
237 len = kx_read (kc, otherside, msg, sizeof(msg));
238 if (len <= 0)
239 errx (1,
240 "error reading initial message from %s: "
241 "this probably means it's using an old version.",
242 host);
243 p = (u_char *)msg;
244 if (*p == ERROR) {
245 p++;
246 p += kx_get_int (p, &tmp, 4, 0);
247 errx (1, "%s: %.*s", host, (int)tmp, p);
248 } else if (*p != ACK) {
249 errx (1, "%s: strange msg %d", host, *p);
250 } else
251 p++;
252 p += kx_get_int (p, &tmp, 4, 0);
253 memcpy(display, p, tmp);
254 display[tmp] = '\0';
255 p += tmp;
257 p += kx_get_int (p, &tmp, 4, 0);
258 memcpy(xauthfile, p, tmp);
259 xauthfile[tmp] = '\0';
260 p += tmp;
262 status_output (kc->debug_flag);
263 for (;;) {
264 pid_t child;
266 len = kx_read (kc, otherside, msg, sizeof(msg));
267 if (len < 0)
268 err (1, "read from %s", host);
269 else if (len == 0)
270 return 0;
272 p = (u_char *)msg;
273 if (*p == ERROR) {
274 p++;
275 p += kx_get_int (p, &tmp, 4, 0);
276 errx (1, "%s: %.*s", host, (int)tmp, p);
277 } else if(*p != NEW_CONN) {
278 errx (1, "%s: strange msg %d", host, *p);
279 } else {
280 p++;
281 p += kx_get_int (p, &tmp, 4, 0);
284 ++nchild;
285 child = fork ();
286 if (child < 0) {
287 warn("fork");
288 continue;
289 } else if (child == 0) {
290 int fd;
291 int xserver;
293 close (otherside);
295 socket_set_port(kc->thataddr, htons(tmp));
297 fd = socket (kc->thataddr->sa_family, SOCK_STREAM, 0);
298 if (fd < 0)
299 err(1, "socket");
300 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
302 int one = 1;
304 setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
305 sizeof(one));
307 #endif
308 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
309 if (kc->keepalive_flag) {
310 int one = 1;
312 setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
313 sizeof(one));
315 #endif
317 if (connect (fd, kc->thataddr, kc->thataddr_len) < 0)
318 err(1, "connect(%s)", host);
320 int d = 0;
321 char *s;
323 s = getenv ("DISPLAY");
324 if (s != NULL) {
325 s = strchr (s, ':');
326 if (s != NULL)
327 d = atoi (s + 1);
330 xserver = connect_local_xsocket (d);
331 if (xserver < 0)
332 return 1;
334 return passive_session (xserver, fd, kc);
335 } else {
341 * Allocate a local pseudo-xserver and wait for connections
344 static int
345 doit_active (kx_context *kc)
347 int otherside;
348 int nsockets;
349 struct x_socket *sockets;
350 u_char msg[1024], *p;
351 int len = strlen(kc->user);
352 int tmp, tmp2;
353 char *str;
354 int i;
355 size_t rem;
356 uint32_t other_port;
357 int error;
358 const char *host = kc->host;
360 otherside = connect_host (kc);
361 if (otherside < 0)
362 return 1;
363 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
364 if (kc->keepalive_flag) {
365 int one = 1;
367 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
368 sizeof(one));
370 #endif
371 p = msg;
372 rem = sizeof(msg);
373 *p++ = INIT;
374 --rem;
375 len = strlen(kc->user);
376 tmp = kx_put_int (len, p, rem, 4);
377 if (tmp < 0)
378 return 1;
379 p += tmp;
380 rem -= tmp;
381 memcpy(p, kc->user, len);
382 p += len;
383 rem -= len;
384 *p++ = (kc->keepalive_flag ? KEEP_ALIVE : 0);
385 --rem;
387 str = getenv("DISPLAY");
388 if (str == NULL || (str = strchr(str, ':')) == NULL)
389 str = ":0";
390 len = strlen (str);
391 tmp = kx_put_int (len, p, rem, 4);
392 if (tmp < 0)
393 return 1;
394 rem -= tmp;
395 p += tmp;
396 memcpy (p, str, len);
397 p += len;
398 rem -= len;
400 str = getenv("XAUTHORITY");
401 if (str == NULL)
402 str = "";
403 len = strlen (str);
404 tmp = kx_put_int (len, p, rem, 4);
405 if (tmp < 0)
406 return 1;
407 p += len;
408 rem -= len;
409 memcpy (p, str, len);
410 p += len;
411 rem -= len;
413 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
414 err (1, "write to %s", host);
416 len = kx_read (kc, otherside, msg, sizeof(msg));
417 if (len < 0)
418 err (1, "read from %s", host);
419 p = (u_char *)msg;
420 if (*p == ERROR) {
421 uint32_t u32;
423 p++;
424 p += kx_get_int (p, &u32, 4, 0);
425 errx (1, "%s: %.*s", host, (int)u32, p);
426 } else if (*p != ACK) {
427 errx (1, "%s: strange msg %d", host, *p);
428 } else
429 p++;
431 tmp2 = get_xsockets (&nsockets, &sockets, kc->tcp_flag);
432 if (tmp2 < 0)
433 return 1;
434 display_num = tmp2;
435 if (kc->tcp_flag)
436 snprintf (display, display_size, "localhost:%u", display_num);
437 else
438 snprintf (display, display_size, ":%u", display_num);
439 error = create_and_write_cookie (xauthfile, xauthfile_size,
440 cookie, cookie_len);
441 if (error) {
442 warnx ("failed creating cookie file: %s", strerror(error));
443 return 1;
445 status_output (kc->debug_flag);
446 for (;;) {
447 fd_set fdset;
448 pid_t child;
449 int fd, thisfd = -1;
450 socklen_t zero = 0;
452 FD_ZERO(&fdset);
453 for (i = 0; i < nsockets; ++i) {
454 if (sockets[i].fd >= FD_SETSIZE)
455 errx (1, "fd too large");
456 FD_SET(sockets[i].fd, &fdset);
458 if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0)
459 continue;
460 for (i = 0; i < nsockets; ++i)
461 if (FD_ISSET(sockets[i].fd, &fdset)) {
462 thisfd = sockets[i].fd;
463 break;
465 fd = accept (thisfd, NULL, &zero);
466 if (fd < 0) {
467 if (errno == EINTR)
468 continue;
469 else
470 err(1, "accept");
473 p = msg;
474 *p++ = NEW_CONN;
475 if (kx_write (kc, otherside, msg, p - msg) != p - msg)
476 err (1, "write to %s", host);
477 len = kx_read (kc, otherside, msg, sizeof(msg));
478 if (len < 0)
479 err (1, "read from %s", host);
480 p = (u_char *)msg;
481 if (*p == ERROR) {
482 uint32_t val;
484 p++;
485 p += kx_get_int (p, &val, 4, 0);
486 errx (1, "%s: %.*s", host, (int)val, p);
487 } else if (*p != NEW_CONN) {
488 errx (1, "%s: strange msg %d", host, *p);
489 } else {
490 p++;
491 p += kx_get_int (p, &other_port, 4, 0);
494 ++nchild;
495 child = fork ();
496 if (child < 0) {
497 warn("fork");
498 continue;
499 } else if (child == 0) {
500 int s;
502 for (i = 0; i < nsockets; ++i)
503 close (sockets[i].fd);
505 close (otherside);
507 socket_set_port(kc->thataddr, htons(tmp));
509 s = socket (kc->thataddr->sa_family, SOCK_STREAM, 0);
510 if (s < 0)
511 err(1, "socket");
512 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
514 int one = 1;
516 setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
517 sizeof(one));
519 #endif
520 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
521 if (kc->keepalive_flag) {
522 int one = 1;
524 setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
525 sizeof(one));
527 #endif
529 if (connect (s, kc->thataddr, kc->thataddr_len) < 0)
530 err(1, "connect");
532 return active_session (fd, s, kc);
533 } else {
534 close (fd);
540 * Should we interpret `disp' as this being a passive call?
543 static int
544 check_for_passive (const char *disp)
546 char local_hostname[MaxHostNameLen];
548 gethostname (local_hostname, sizeof(local_hostname));
550 return disp != NULL &&
551 (*disp == ':'
552 || strncmp(disp, "unix", 4) == 0
553 || strncmp(disp, "localhost", 9) == 0
554 || strncmp(disp, local_hostname, strlen(local_hostname)) == 0);
558 * Set up signal handlers and then call the functions.
561 static int
562 doit (kx_context *kc, int passive_flag)
564 signal (SIGCHLD, childhandler);
565 signal (SIGUSR1, usr1handler);
566 signal (SIGUSR2, usr2handler);
567 if (passive_flag)
568 return doit_passive (kc);
569 else
570 return doit_active (kc);
573 #ifdef KRB4
576 * Start a v4-authenticatated kx connection.
579 static int
580 doit_v4 (const char *host, int port, const char *user,
581 int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag)
583 int ret;
584 kx_context context;
586 krb4_make_context (&context);
587 context_set (&context,
588 host, user, port, debug_flag, keepalive_flag, tcp_flag);
590 ret = doit (&context, passive_flag);
591 context_destroy (&context);
592 return ret;
594 #endif /* KRB4 */
596 #ifdef KRB5
599 * Start a v5-authenticatated kx connection.
602 static int
603 doit_v5 (const char *host, int port, const char *user,
604 int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag)
606 int ret;
607 kx_context context;
609 krb5_make_context (&context);
610 context_set (&context,
611 host, user, port, debug_flag, keepalive_flag, tcp_flag);
613 ret = doit (&context, passive_flag);
614 context_destroy (&context);
615 return ret;
617 #endif /* KRB5 */
620 * Variables set from the arguments
623 #ifdef KRB4
624 static int use_v4 = -1;
625 #ifdef HAVE_KRB_ENABLE_DEBUG
626 static int krb_debug_flag = 0;
627 #endif /* HAVE_KRB_ENABLE_DEBUG */
628 #endif /* KRB4 */
629 #ifdef KRB5
630 static int use_v5 = -1;
631 #endif
632 static char *port_str = NULL;
633 static const char *user = NULL;
634 static int tcp_flag = 0;
635 static int passive_flag = 0;
636 static int keepalive_flag = 1;
637 static int debug_flag = 0;
638 static int version_flag = 0;
639 static int help_flag = 0;
641 struct getargs args[] = {
642 #ifdef KRB4
643 { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4",
644 NULL },
645 #ifdef HAVE_KRB_ENABLE_DEBUG
646 { "krb4-debug", 'D', arg_flag, &krb_debug_flag,
647 "enable krb4 debugging" },
648 #endif /* HAVE_KRB_ENABLE_DEBUG */
649 #endif /* KRB4 */
650 #ifdef KRB5
651 { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5",
652 NULL },
653 #endif
654 { "port", 'p', arg_string, &port_str, "Use this port",
655 "number-of-service" },
656 { "user", 'l', arg_string, &user, "Run as this user",
657 NULL },
658 { "tcp", 't', arg_flag, &tcp_flag,
659 "Use a TCP connection for X11" },
660 { "passive", 'P', arg_flag, &passive_flag,
661 "Force a passive connection" },
662 { "keepalive", 'k', arg_negative_flag, &keepalive_flag,
663 "disable keep-alives" },
664 { "debug", 'd', arg_flag, &debug_flag,
665 "Enable debug information" },
666 { "version", 0, arg_flag, &version_flag, "Print version",
667 NULL },
668 { "help", 0, arg_flag, &help_flag, NULL,
669 NULL }
672 static void
673 usage(int ret)
675 arg_printusage (args,
676 sizeof(args) / sizeof(args[0]),
677 NULL,
678 "host");
679 exit (ret);
683 * kx - forward an x-connection over a kerberos-encrypted channel.
687 main(int argc, char **argv)
689 int port = 0;
690 int optidx = 0;
691 int ret = 1;
692 char *host = NULL;
694 setprogname (argv[0]);
696 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
697 &optidx))
698 usage (1);
700 if (help_flag)
701 usage (0);
703 if (version_flag) {
704 print_version (NULL);
705 return 0;
708 if (optidx != argc - 1)
709 usage (1);
711 host = argv[optidx];
713 if (port_str) {
714 struct servent *s = roken_getservbyname (port_str, "tcp");
716 if (s)
717 port = s->s_port;
718 else {
719 char *ptr;
721 port = strtol (port_str, &ptr, 10);
722 if (port == 0 && ptr == port_str)
723 errx (1, "Bad port `%s'", port_str);
724 port = htons(port);
728 if (user == NULL) {
729 user = get_default_username ();
730 if (user == NULL)
731 errx (1, "who are you?");
734 if (!passive_flag)
735 passive_flag = check_for_passive (getenv("DISPLAY"));
737 #if defined(HAVE_KERNEL_ENABLE_DEBUG)
738 if (krb_debug_flag)
739 krb_enable_debug ();
740 #endif
742 #if defined(KRB4) && defined(KRB5)
743 if(use_v4 == -1 && use_v5 == 1)
744 use_v4 = 0;
745 if(use_v5 == -1 && use_v4 == 1)
746 use_v5 = 0;
747 #endif
749 #ifdef KRB5
750 if (ret && use_v5) {
751 if (port == 0)
752 port = krb5_getportbyname(NULL, "kx", "tcp", KX_PORT);
753 ret = doit_v5 (host, port, user,
754 passive_flag, debug_flag, keepalive_flag, tcp_flag);
756 #endif
757 #ifdef KRB4
758 if (ret && use_v4) {
759 if (port == 0)
760 port = k_getportbyname("kx", "tcp", htons(KX_PORT));
761 ret = doit_v4 (host, port, user,
762 passive_flag, debug_flag, keepalive_flag, tcp_flag);
764 #endif
765 return ret;