2 * Copyright (c) 1995, 1996, 1997 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. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * Signal handler that justs waits for the children when they die.
51 childhandler (int sig
)
57 pid
= waitpid (-1, &status
, WNOHANG
|WUNTRACED
);
58 if (pid
> 0 && (WIFEXITED(status
) || WIFSIGNALED(status
)))
59 if (--nchild
== 0 && donep
)
62 signal (SIGCHLD
, childhandler
);
67 * Handler for SIGUSR1.
68 * This signal means that we should wait until there are no children
81 * Almost the same as for SIGUSR1, except we should exit immediately
82 * if there are no active children.
96 * Establish authenticated connection
100 connect_host (char *host
, char *user
, des_cblock
*key
,
101 des_key_schedule schedule
, int port
,
102 struct sockaddr_in
*thisaddr
,
103 struct sockaddr_in
*thataddr
)
110 struct hostent
*hostent
;
114 hostent
= gethostbyname (host
);
115 if (hostent
== NULL
) {
116 warnx ("gethostbyname '%s' failed: %s", host
,
126 memset (thataddr
, 0, sizeof(*thataddr
));
127 thataddr
->sin_family
= AF_INET
;
128 thataddr
->sin_port
= port
;
129 for(p
= hostent
->h_addr_list
; *p
; ++p
) {
130 memcpy (&thataddr
->sin_addr
, *p
, sizeof(thataddr
->sin_addr
));
132 s
= socket (AF_INET
, SOCK_STREAM
, 0);
136 if (connect (s
, (struct sockaddr
*)thataddr
, sizeof(*thataddr
)) < 0) {
137 warn ("connect(%s)", host
);
147 addrlen
= sizeof(*thisaddr
);
148 if (getsockname (s
, (struct sockaddr
*)thisaddr
, &addrlen
) < 0 ||
149 addrlen
!= sizeof(*thisaddr
))
150 err(1, "getsockname(%s)", host
);
151 status
= krb_sendauth (KOPT_DO_MUTUAL
, s
, &text
, "rcmd",
152 host
, krb_realmofhost (host
),
153 getpid(), &msg
, &cred
, schedule
,
154 thisaddr
, thataddr
, KX_VERSION
);
155 if (status
!= KSUCCESS
) {
156 warnx ("%s: %s\n", host
, krb_get_err_text(status
));
159 memcpy(key
, cred
.session
, sizeof(des_cblock
));
164 * Get rid of the cookie that we were sent and get the correct one
165 * from our own cookie file instead.
169 passive_session (int xserver
, int fd
, des_cblock
*iv
,
170 des_key_schedule schedule
)
172 if (replace_cookie (xserver
, fd
, XauFileName()))
175 return copy_encrypted (xserver
, fd
, iv
, schedule
);
179 active_session (int xserver
, int fd
, des_cblock
*iv
,
180 des_key_schedule schedule
)
182 if (verify_and_remove_cookies (xserver
, fd
))
185 return copy_encrypted (xserver
, fd
, iv
, schedule
);
189 status_output (int debugp
)
192 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display
, xauthfile
);
199 } else if (pid
> 0) {
200 printf ("%u\t%s\t%s\n", (unsigned)pid
, display
, xauthfile
);
209 * Obtain an authenticated connection to `host' on `port'. Send a kx
210 * message saying we are `user' and want to use passive mode. Wait
211 * for answer on that connection and fork of a child for every new
212 * connection we have to make.
216 doit_passive (char *host
, char *user
, int debugp
, int keepalivep
,
219 des_key_schedule schedule
;
222 struct sockaddr_in me
, him
;
223 u_char msg
[1024], *p
;
228 otherside
= connect_host (host
, user
, &key
, schedule
, port
,
232 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
236 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
244 p
+= krb_put_int (len
, p
, 4);
245 strncpy(p
, user
, len
);
247 *p
++ = PASSIVE
| (keepalivep
? KEEP_ALIVE
: 0);
248 if (write_encrypted (otherside
, msg
, p
- msg
, schedule
,
249 &key
, &me
, &him
) < 0)
250 err (1, "write to %s", host
);
251 len
= read_encrypted (otherside
, msg
, sizeof(msg
), &ret
,
252 schedule
, &key
, &him
, &me
);
255 "error reading initial message from %s: "
256 "this probably means it's using an old version.",
261 p
+= krb_get_int (p
, &tmp
, 4, 0);
262 errx (1, "%s: %.*s", host
, (int)tmp
, p
);
263 } else if (*p
!= ACK
) {
264 errx (1, "%s: strange msg %d", host
, *p
);
267 p
+= krb_get_int (p
, &tmp
, 4, 0);
268 strncpy(display
, p
, tmp
);
272 p
+= krb_get_int (p
, &tmp
, 4, 0);
273 strncpy(xauthfile
, p
, tmp
);
274 xauthfile
[tmp
] = '\0';
277 status_output (debugp
);
281 len
= read_encrypted (otherside
, msg
, sizeof(msg
), &ret
,
282 schedule
, &key
, &him
, &me
);
284 err (1, "read from %s", host
);
291 p
+= krb_get_int (p
, &tmp
, 4, 0);
292 errx (1, "%s: %.*s", host
, (int)tmp
, p
);
293 } else if(*p
!= NEW_CONN
) {
294 errx (1, "%s: strange msg %d", host
, *p
);
297 p
+= krb_get_int (p
, &tmp
, 4, 0);
305 } else if (child
== 0) {
306 struct sockaddr_in addr
;
313 addr
.sin_port
= htons(tmp
);
314 fd
= socket (AF_INET
, SOCK_STREAM
, 0);
317 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
321 setsockopt (fd
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
325 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
329 setsockopt (fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
334 if (connect (fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
335 err(1, "connect(%s)", host
);
336 xserver
= connect_local_xsocket (0);
339 return passive_session (xserver
, fd
, &key
, schedule
);
346 * Allocate a local pseudo-xserver and wait for connections
350 doit_active (char *host
, char *user
,
351 int debugpp
, int keepalivep
, int tcpp
, int port
)
353 des_key_schedule schedule
;
356 int rendez_vous1
= 0, rendez_vous2
= 0;
357 struct sockaddr_in me
, him
;
358 u_char msg
[1024], *p
;
359 int len
= strlen(user
);
364 otherside
= connect_host (host
, user
, &key
, schedule
, port
,
368 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
372 setsockopt (otherside
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
379 p
+= krb_put_int (len
, p
, 4);
380 strncpy(p
, user
, len
);
382 *p
++ = (keepalivep
? KEEP_ALIVE
: 0);
384 s
= getenv("DISPLAY");
385 if (s
== NULL
|| (s
= strchr(s
, ':')) == NULL
)
388 p
+= krb_put_int (len
, p
, 4);
392 s
= getenv("XAUTHORITY");
396 p
+= krb_put_int (len
, p
, 4);
400 if (write_encrypted (otherside
, msg
, p
- msg
, schedule
,
401 &key
, &me
, &him
) < 0)
402 err (1, "write to %s", host
);
404 len
= read_encrypted (otherside
, msg
, sizeof(msg
), &ret
,
405 schedule
, &key
, &him
, &me
);
407 err (1, "read from %s", host
);
411 p
+= krb_get_int (p
, &tmp
, 4, 0);
412 errx (1, "%s: %.*s", host
, (int)tmp
, p
);
413 } else if (*p
!= ACK
) {
414 errx (1, "%s: strange msg %d", host
, *p
);
418 tmp
= get_xsockets (&rendez_vous1
,
419 tcpp
? &rendez_vous2
: NULL
);
424 snprintf (display
, display_size
, "localhost:%u", display_num
);
426 snprintf (display
, display_size
, ":%u", display_num
);
427 strncpy(xauthfile
, tempnam("/tmp", NULL
), xauthfile_size
);
428 if (create_and_write_cookie (xauthfile
, cookie
, cookie_len
))
430 status_output (debugpp
);
439 FD_SET(rendez_vous1
, &fdset
);
441 FD_SET(rendez_vous2
, &fdset
);
442 if (select(FD_SETSIZE
, &fdset
, NULL
, NULL
, NULL
) <= 0)
444 if (rendez_vous1
&& FD_ISSET(rendez_vous1
, &fdset
))
445 thisfd
= rendez_vous1
;
446 else if (rendez_vous2
&& FD_ISSET(rendez_vous2
, &fdset
))
447 thisfd
= rendez_vous2
;
451 fd
= accept (thisfd
, NULL
, &zero
);
460 if (write_encrypted (otherside
, msg
, p
- msg
, schedule
,
461 &key
, &me
, &him
) < 0)
462 err (1, "write to %s", host
);
463 len
= read_encrypted (otherside
, msg
, sizeof(msg
), &ret
,
464 schedule
, &key
, &him
, &me
);
466 err (1, "read from %s", host
);
470 p
+= krb_get_int (p
, &tmp
, 4, 0);
471 errx (1, "%s: %.*s", host
, (int)tmp
, p
);
472 } else if (*p
!= NEW_CONN
) {
473 errx (1, "%s: strange msg %d", host
, *p
);
476 p
+= krb_get_int (p
, &tmp
, 4, 0);
484 } else if (child
== 0) {
486 struct sockaddr_in addr
;
489 close (rendez_vous1
);
491 close (rendez_vous2
);
496 addr
.sin_port
= htons(tmp
);
497 s
= socket (AF_INET
, SOCK_STREAM
, 0);
500 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
504 setsockopt (s
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&one
,
508 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
512 setsockopt (s
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&one
,
517 if (connect (s
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
520 return active_session (fd
, s
, &key
, schedule
);
532 check_for_passive (const char *disp
)
534 char local_hostname
[MaxHostNameLen
];
536 gethostname (local_hostname
, sizeof(local_hostname
));
538 return disp
!= NULL
&&
540 || strncmp(disp
, "unix", 4) == 0
541 || strncmp(disp
, "localhost", 9) == 0
542 || strncmp(disp
, local_hostname
, strlen(local_hostname
) == 0));
548 fprintf (stderr
, "Usage: %s [-p port] [-d] [-t] [-l remoteuser] host\n",
554 * kx - forward x connection over a kerberos-encrypted channel.
559 main(int argc
, char **argv
)
561 int force_passive
= 0;
564 int debugp
= 0, tcpp
= 0;
568 set_progname (argv
[0]);
569 while((c
= getopt(argc
, argv
, "ktdl:p:P")) != EOF
) {
584 port
= htons(atoi (optarg
));
601 struct passwd
*p
= k_getpwuid (getuid ());
603 errx(1, "Who are you?");
604 user
= strdup (p
->pw_name
);
607 port
= k_getportbyname ("kx", "tcp", htons(KX_PORT
));
608 signal (SIGCHLD
, childhandler
);
609 signal (SIGUSR1
, usr1handler
);
610 signal (SIGUSR2
, usr2handler
);
611 if (check_for_passive(getenv("DISPLAY")))
612 return doit_passive (argv
[0], user
, debugp
, keepalivep
, port
);
614 return doit_active (argv
[0], user
, debugp
, keepalivep
, tcpp
, port
);