2 * Copyright (c) 1995-2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
42 * Signal handler that justs waits for the children when they die.
46 childhandler (int sig
)
52 pid
= waitpid (-1, &status
, WNOHANG
|WUNTRACED
);
53 if (pid
> 0 && (WIFEXITED(status
) || WIFSIGNALED(status
)))
54 if (--nchild
== 0 && donep
)
57 signal (SIGCHLD
, childhandler
);
62 * Handler for SIGUSR1.
63 * This signal means that we should wait until there are no children
76 * Almost the same as for SIGUSR1, except we should exit immediately
77 * if there are no active children.
91 * Establish authenticated connection. Return socket or -1.
95 connect_host (kx_context
*kc
)
97 struct addrinfo
*ai
, *a
;
98 struct addrinfo hints
;
100 char portstr
[NI_MAXSERV
];
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
);
114 warnx ("%s: %s", kc
->host
, gai_strerror(error
));
118 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
119 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
122 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
123 warn ("connect(%s)", kc
->host
);
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
;
144 if ((*kc
->authenticate
)(kc
, 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
156 passive_session (int xserver
, int fd
, kx_context
*kc
)
158 if (replace_cookie (xserver
, fd
, XauFileName(), 1))
161 return copy_encrypted (kc
, xserver
, fd
);
165 active_session (int xserver
, int fd
, kx_context
*kc
)
167 if (verify_and_remove_cookies (xserver
, fd
, 1))
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.
179 status_output (int debugp
)
182 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display
, xauthfile
);
189 } else if (pid
> 0) {
190 printf ("%u\t%s\t%s\n", (unsigned)pid
, display
, xauthfile
);
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.
206 doit_passive (kx_context
*kc
)
209 u_char msg
[1024], *p
;
212 const char *host
= kc
->host
;
214 otherside
= connect_host (kc
);
218 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
219 if (kc
->keepalive_flag
) {
222 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
229 len
= strlen(kc
->user
);
230 p
+= kx_put_int (len
, p
, sizeof(msg
) - 1, 4);
231 memcpy(p
, kc
->user
, 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
));
239 "error reading initial message from %s: "
240 "this probably means it's using an old version.",
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
);
251 p
+= kx_get_int (p
, &tmp
, 4, 0);
252 memcpy(display
, p
, tmp
);
256 p
+= kx_get_int (p
, &tmp
, 4, 0);
257 memcpy(xauthfile
, p
, tmp
);
258 xauthfile
[tmp
] = '\0';
261 status_output (kc
->debug_flag
);
265 len
= kx_read (kc
, otherside
, msg
, sizeof(msg
));
267 err (1, "read from %s", host
);
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
);
280 p
+= kx_get_int (p
, &tmp
, 4, 0);
288 } else if (child
== 0) {
294 socket_set_port(kc
->thataddr
, htons(tmp
));
296 fd
= socket (kc
->thataddr
->sa_family
, SOCK_STREAM
, 0);
299 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
303 setsockopt (fd
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
307 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
308 if (kc
->keepalive_flag
) {
311 setsockopt (fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
316 if (connect (fd
, kc
->thataddr
, kc
->thataddr_len
) < 0)
317 err(1, "connect(%s)", host
);
322 s
= getenv ("DISPLAY");
329 xserver
= connect_local_xsocket (d
);
333 return passive_session (xserver
, fd
, kc
);
340 * Allocate a local pseudo-xserver and wait for connections
344 doit_active (kx_context
*kc
)
348 struct x_socket
*sockets
;
349 u_char msg
[1024], *p
;
350 int len
= strlen(kc
->user
);
357 const char *host
= kc
->host
;
359 otherside
= connect_host (kc
);
362 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
363 if (kc
->keepalive_flag
) {
366 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
374 len
= strlen(kc
->user
);
375 tmp
= kx_put_int (len
, p
, rem
, 4);
380 memcpy(p
, kc
->user
, len
);
383 *p
++ = (kc
->keepalive_flag
? KEEP_ALIVE
: 0);
386 str
= getenv("DISPLAY");
387 if (str
== NULL
|| (str
= strchr(str
, ':')) == NULL
)
390 tmp
= kx_put_int (len
, p
, rem
, 4);
395 memcpy (p
, str
, len
);
399 str
= getenv("XAUTHORITY");
403 tmp
= kx_put_int (len
, p
, rem
, 4);
408 memcpy (p
, str
, 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
));
417 err (1, "read from %s", host
);
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
);
430 tmp2
= get_xsockets (&nsockets
, &sockets
, kc
->tcp_flag
);
435 snprintf (display
, display_size
, "localhost:%u", display_num
);
437 snprintf (display
, display_size
, ":%u", display_num
);
438 error
= create_and_write_cookie (xauthfile
, xauthfile_size
,
441 warnx ("failed creating cookie file: %s", strerror(error
));
444 status_output (kc
->debug_flag
);
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)
459 for (i
= 0; i
< nsockets
; ++i
)
460 if (FD_ISSET(sockets
[i
].fd
, &fdset
)) {
461 thisfd
= sockets
[i
].fd
;
464 fd
= accept (thisfd
, NULL
, &zero
);
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
));
478 err (1, "read from %s", host
);
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
);
490 p
+= kx_get_int (p
, &other_port
, 4, 0);
498 } else if (child
== 0) {
501 for (i
= 0; i
< nsockets
; ++i
)
502 close (sockets
[i
].fd
);
506 socket_set_port(kc
->thataddr
, htons(tmp
));
508 s
= socket (kc
->thataddr
->sa_family
, SOCK_STREAM
, 0);
511 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
515 setsockopt (s
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
519 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
520 if (kc
->keepalive_flag
) {
523 setsockopt (s
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
528 if (connect (s
, kc
->thataddr
, kc
->thataddr_len
) < 0)
531 return active_session (fd
, s
, kc
);
539 * Should we interpret `disp' as this being a passive call?
543 check_for_passive (const char *disp
)
545 char local_hostname
[MaxHostNameLen
];
547 gethostname (local_hostname
, sizeof(local_hostname
));
549 return disp
!= NULL
&&
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.
561 doit (kx_context
*kc
, int passive_flag
)
563 signal (SIGCHLD
, childhandler
);
564 signal (SIGUSR1
, usr1handler
);
565 signal (SIGUSR2
, usr2handler
);
567 return doit_passive (kc
);
569 return doit_active (kc
);
575 * Start a v4-authenticatated kx connection.
579 doit_v4 (const char *host
, int port
, const char *user
,
580 int passive_flag
, int debug_flag
, int keepalive_flag
, int tcp_flag
)
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
);
598 * Start a v5-authenticatated kx connection.
602 doit_v5 (const char *host
, int port
, const char *user
,
603 int passive_flag
, int debug_flag
, int keepalive_flag
, int tcp_flag
)
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
);
619 * Variables set from the arguments
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 */
629 static int use_v5
= -1;
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
[] = {
642 { "krb4", '4', arg_flag
, &use_v4
, "Use Kerberos V4",
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 */
650 { "krb5", '5', arg_flag
, &use_v5
, "Use Kerberos V5",
653 { "port", 'p', arg_string
, &port_str
, "Use this port",
654 "number-of-service" },
655 { "user", 'l', arg_string
, &user
, "Run as this user",
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",
667 { "help", 0, arg_flag
, &help_flag
, NULL
,
674 arg_printusage (args
,
675 sizeof(args
) / sizeof(args
[0]),
682 * kx - forward an x-connection over a kerberos-encrypted channel.
686 main(int argc
, char **argv
)
693 setprogname (argv
[0]);
695 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
703 print_version (NULL
);
707 if (optidx
!= argc
- 1)
713 struct servent
*s
= roken_getservbyname (port_str
, "tcp");
720 port
= strtol (port_str
, &ptr
, 10);
721 if (port
== 0 && ptr
== port_str
)
722 errx (1, "Bad port `%s'", port_str
);
728 user
= get_default_username ();
730 errx (1, "who are you?");
734 passive_flag
= check_for_passive (getenv("DISPLAY"));
736 #if defined(HAVE_KERNEL_ENABLE_DEBUG)
741 #if defined(KRB4) && defined(KRB5)
742 if(use_v4
== -1 && use_v5
== 1)
744 if(use_v5
== -1 && use_v4
== 1)
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
);
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
);