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
36 __RCSID("$Heimdal: kx.c 20452 2007-04-19 20:04:19Z lha $"
43 * Signal handler that justs waits for the children when they die.
47 childhandler (int sig
)
53 pid
= waitpid (-1, &status
, WNOHANG
|WUNTRACED
);
54 if (pid
> 0 && (WIFEXITED(status
) || WIFSIGNALED(status
)))
55 if (--nchild
== 0 && donep
)
58 signal (SIGCHLD
, childhandler
);
63 * Handler for SIGUSR1.
64 * This signal means that we should wait until there are no children
77 * Almost the same as for SIGUSR1, except we should exit immediately
78 * if there are no active children.
92 * Establish authenticated connection. Return socket or -1.
96 connect_host (kx_context
*kc
)
98 struct addrinfo
*ai
, *a
;
99 struct addrinfo hints
;
101 char portstr
[NI_MAXSERV
];
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
);
115 warnx ("%s: %s", kc
->host
, gai_strerror(error
));
119 for (a
= ai
; a
!= NULL
; a
= a
->ai_next
) {
120 s
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
123 if (connect (s
, a
->ai_addr
, a
->ai_addrlen
) < 0) {
124 warn ("connect(%s)", kc
->host
);
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
;
145 if ((*kc
->authenticate
)(kc
, 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
157 passive_session (int xserver
, int fd
, kx_context
*kc
)
159 if (replace_cookie (xserver
, fd
, XauFileName(), 1))
162 return copy_encrypted (kc
, xserver
, fd
);
166 active_session (int xserver
, int fd
, kx_context
*kc
)
168 if (verify_and_remove_cookies (xserver
, fd
, 1))
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.
180 status_output (int debugp
)
183 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display
, xauthfile
);
190 } else if (pid
> 0) {
191 printf ("%u\t%s\t%s\n", (unsigned)pid
, display
, xauthfile
);
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.
207 doit_passive (kx_context
*kc
)
210 u_char msg
[1024], *p
;
213 const char *host
= kc
->host
;
215 otherside
= connect_host (kc
);
219 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
220 if (kc
->keepalive_flag
) {
223 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
230 len
= strlen(kc
->user
);
231 p
+= kx_put_int (len
, p
, sizeof(msg
) - 1, 4);
232 memcpy(p
, kc
->user
, 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
));
240 "error reading initial message from %s: "
241 "this probably means it's using an old version.",
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
);
252 p
+= kx_get_int (p
, &tmp
, 4, 0);
253 memcpy(display
, p
, tmp
);
257 p
+= kx_get_int (p
, &tmp
, 4, 0);
258 memcpy(xauthfile
, p
, tmp
);
259 xauthfile
[tmp
] = '\0';
262 status_output (kc
->debug_flag
);
266 len
= kx_read (kc
, otherside
, msg
, sizeof(msg
));
268 err (1, "read from %s", host
);
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
);
281 p
+= kx_get_int (p
, &tmp
, 4, 0);
289 } else if (child
== 0) {
295 socket_set_port(kc
->thataddr
, htons(tmp
));
297 fd
= socket (kc
->thataddr
->sa_family
, SOCK_STREAM
, 0);
300 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
304 setsockopt (fd
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
308 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
309 if (kc
->keepalive_flag
) {
312 setsockopt (fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
317 if (connect (fd
, kc
->thataddr
, kc
->thataddr_len
) < 0)
318 err(1, "connect(%s)", host
);
323 s
= getenv ("DISPLAY");
330 xserver
= connect_local_xsocket (d
);
334 return passive_session (xserver
, fd
, kc
);
341 * Allocate a local pseudo-xserver and wait for connections
345 doit_active (kx_context
*kc
)
349 struct x_socket
*sockets
;
350 u_char msg
[1024], *p
;
351 int len
= strlen(kc
->user
);
358 const char *host
= kc
->host
;
360 otherside
= connect_host (kc
);
363 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
364 if (kc
->keepalive_flag
) {
367 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
375 len
= strlen(kc
->user
);
376 tmp
= kx_put_int (len
, p
, rem
, 4);
381 memcpy(p
, kc
->user
, len
);
384 *p
++ = (kc
->keepalive_flag
? KEEP_ALIVE
: 0);
387 str
= getenv("DISPLAY");
388 if (str
== NULL
|| (str
= strchr(str
, ':')) == NULL
)
391 tmp
= kx_put_int (len
, p
, rem
, 4);
396 memcpy (p
, str
, len
);
400 str
= getenv("XAUTHORITY");
404 tmp
= kx_put_int (len
, p
, rem
, 4);
409 memcpy (p
, str
, 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
));
418 err (1, "read from %s", host
);
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
);
431 tmp2
= get_xsockets (&nsockets
, &sockets
, kc
->tcp_flag
);
436 snprintf (display
, display_size
, "localhost:%u", display_num
);
438 snprintf (display
, display_size
, ":%u", display_num
);
439 error
= create_and_write_cookie (xauthfile
, xauthfile_size
,
442 warnx ("failed creating cookie file: %s", strerror(error
));
445 status_output (kc
->debug_flag
);
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)
460 for (i
= 0; i
< nsockets
; ++i
)
461 if (FD_ISSET(sockets
[i
].fd
, &fdset
)) {
462 thisfd
= sockets
[i
].fd
;
465 fd
= accept (thisfd
, NULL
, &zero
);
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
));
479 err (1, "read from %s", host
);
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
);
491 p
+= kx_get_int (p
, &other_port
, 4, 0);
499 } else if (child
== 0) {
502 for (i
= 0; i
< nsockets
; ++i
)
503 close (sockets
[i
].fd
);
507 socket_set_port(kc
->thataddr
, htons(tmp
));
509 s
= socket (kc
->thataddr
->sa_family
, SOCK_STREAM
, 0);
512 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
516 setsockopt (s
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
520 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
521 if (kc
->keepalive_flag
) {
524 setsockopt (s
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
529 if (connect (s
, kc
->thataddr
, kc
->thataddr_len
) < 0)
532 return active_session (fd
, s
, kc
);
540 * Should we interpret `disp' as this being a passive call?
544 check_for_passive (const char *disp
)
546 char local_hostname
[MaxHostNameLen
];
548 gethostname (local_hostname
, sizeof(local_hostname
));
550 return disp
!= NULL
&&
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.
562 doit (kx_context
*kc
, int passive_flag
)
564 signal (SIGCHLD
, childhandler
);
565 signal (SIGUSR1
, usr1handler
);
566 signal (SIGUSR2
, usr2handler
);
568 return doit_passive (kc
);
570 return doit_active (kc
);
576 * Start a v4-authenticatated kx connection.
580 doit_v4 (const char *host
, int port
, const char *user
,
581 int passive_flag
, int debug_flag
, int keepalive_flag
, int tcp_flag
)
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
);
599 * Start a v5-authenticatated kx connection.
603 doit_v5 (const char *host
, int port
, const char *user
,
604 int passive_flag
, int debug_flag
, int keepalive_flag
, int tcp_flag
)
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
);
620 * Variables set from the arguments
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 */
630 static int use_v5
= -1;
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
[] = {
643 { "krb4", '4', arg_flag
, &use_v4
, "Use Kerberos V4",
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 */
651 { "krb5", '5', arg_flag
, &use_v5
, "Use Kerberos V5",
654 { "port", 'p', arg_string
, &port_str
, "Use this port",
655 "number-of-service" },
656 { "user", 'l', arg_string
, &user
, "Run as this user",
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",
668 { "help", 0, arg_flag
, &help_flag
, NULL
,
675 arg_printusage (args
,
676 sizeof(args
) / sizeof(args
[0]),
683 * kx - forward an x-connection over a kerberos-encrypted channel.
687 main(int argc
, char **argv
)
694 setprogname (argv
[0]);
696 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
,
704 print_version (NULL
);
708 if (optidx
!= argc
- 1)
714 struct servent
*s
= roken_getservbyname (port_str
, "tcp");
721 port
= strtol (port_str
, &ptr
, 10);
722 if (port
== 0 && ptr
== port_str
)
723 errx (1, "Bad port `%s'", port_str
);
729 user
= get_default_username ();
731 errx (1, "who are you?");
735 passive_flag
= check_for_passive (getenv("DISPLAY"));
737 #if defined(HAVE_KERNEL_ENABLE_DEBUG)
742 #if defined(KRB4) && defined(KRB5)
743 if(use_v4
== -1 && use_v5
== 1)
745 if(use_v5
== -1 && use_v4
== 1)
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
);
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
);