*** empty log message ***
[heimdal.git] / appl / kx / kx.c
blobcf2acd1d330f8852651710206b8a333474b8c3b5
1 /*
2 * Copyright (c) 1995, 1996, 1997 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. 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
36 * SUCH DAMAGE.
39 #include "kx.h"
41 RCSID("$Id$");
43 static int nchild;
44 static int donep;
47 * Signal handler that justs waits for the children when they die.
50 static RETSIGTYPE
51 childhandler (int sig)
53 pid_t pid;
54 int status;
56 do {
57 pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
58 if (pid > 0 && (WIFEXITED(status) || WIFSIGNALED(status)))
59 if (--nchild == 0 && donep)
60 exit (0);
61 } while(pid > 0);
62 signal (SIGCHLD, childhandler);
63 SIGRETURN(0);
67 * Handler for SIGUSR1.
68 * This signal means that we should wait until there are no children
69 * left and then exit.
72 static RETSIGTYPE
73 usr1handler (int sig)
75 donep = 1;
77 SIGRETURN(0);
81 * Almost the same as for SIGUSR1, except we should exit immediately
82 * if there are no active children.
85 static RETSIGTYPE
86 usr2handler (int sig)
88 donep = 1;
89 if (nchild == 0)
90 exit (0);
92 SIGRETURN(0);
96 * Establish authenticated connection
99 static int
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)
105 CREDENTIALS cred;
106 KTEXT_ST text;
107 MSG_DAT msg;
108 int status;
109 int addrlen;
110 struct hostent *hostent;
111 int s;
112 char **p;
114 hostent = gethostbyname (host);
115 if (hostent == NULL) {
116 warnx ("gethostbyname '%s' failed: %s", host,
117 #ifdef HAVE_H_ERRNO
118 hstrerror(h_errno)
119 #else
120 "unknown error"
121 #endif
123 return -1;
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);
133 if (s < 0)
134 err (1, "socket");
136 if (connect (s, (struct sockaddr *)thataddr, sizeof(*thataddr)) < 0) {
137 warn ("connect(%s)", host);
138 close (s);
139 continue;
140 } else {
141 break;
144 if (*p == NULL)
145 return -1;
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));
157 return -1;
159 memcpy(key, cred.session, sizeof(des_cblock));
160 return s;
164 * Get rid of the cookie that we were sent and get the correct one
165 * from our own cookie file instead.
168 static int
169 passive_session (int xserver, int fd, des_cblock *iv,
170 des_key_schedule schedule)
172 if (replace_cookie (xserver, fd, XauFileName()))
173 return 1;
174 else
175 return copy_encrypted (xserver, fd, iv, schedule);
178 static int
179 active_session (int xserver, int fd, des_cblock *iv,
180 des_key_schedule schedule)
182 if (verify_and_remove_cookies (xserver, fd))
183 return 1;
184 else
185 return copy_encrypted (xserver, fd, iv, schedule);
188 static void
189 status_output (int debugp)
191 if(debugp)
192 printf ("%u\t%s\t%s\n", (unsigned)getpid(), display, xauthfile);
193 else {
194 pid_t pid;
196 pid = fork();
197 if (pid < 0) {
198 err(1, "fork");
199 } else if (pid > 0) {
200 printf ("%u\t%s\t%s\n", (unsigned)pid, display, xauthfile);
201 exit (0);
202 } else {
203 fclose(stdout);
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.
215 static int
216 doit_passive (char *host, char *user, int debugp, int keepalivep,
217 int port)
219 des_key_schedule schedule;
220 des_cblock key;
221 int otherside;
222 struct sockaddr_in me, him;
223 u_char msg[1024], *p;
224 int len;
225 void *ret;
226 u_int32_t tmp;
228 otherside = connect_host (host, user, &key, schedule, port,
229 &me, &him);
230 if (otherside < 0)
231 return 1;
232 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
233 if (keepalivep) {
234 int one = 1;
236 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
237 sizeof(one));
239 #endif
241 p = msg;
242 *p++ = INIT;
243 len = strlen(user);
244 p += krb_put_int (len, p, 4);
245 strncpy(p, user, len);
246 p += 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);
253 if (len <= 0)
254 errx (1,
255 "error reading initial message from %s: "
256 "this probably means it's using an old version.",
257 host);
258 p = (u_char *)ret;
259 if (*p == ERROR) {
260 p++;
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);
265 } else
266 p++;
267 p += krb_get_int (p, &tmp, 4, 0);
268 strncpy(display, p, tmp);
269 display[tmp] = '\0';
270 p += tmp;
272 p += krb_get_int (p, &tmp, 4, 0);
273 strncpy(xauthfile, p, tmp);
274 xauthfile[tmp] = '\0';
275 p += tmp;
277 status_output (debugp);
278 for (;;) {
279 pid_t child;
281 len = read_encrypted (otherside, msg, sizeof(msg), &ret,
282 schedule, &key, &him, &me);
283 if (len < 0)
284 err (1, "read from %s", host);
285 else if (len == 0)
286 return 0;
288 p = (u_char *)ret;
289 if (*p == ERROR) {
290 p++;
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);
295 } else {
296 p++;
297 p += krb_get_int (p, &tmp, 4, 0);
300 ++nchild;
301 child = fork ();
302 if (child < 0) {
303 warn("fork");
304 continue;
305 } else if (child == 0) {
306 struct sockaddr_in addr;
307 int fd;
308 int xserver;
310 addr = him;
311 close (otherside);
313 addr.sin_port = htons(tmp);
314 fd = socket (AF_INET, SOCK_STREAM, 0);
315 if (fd < 0)
316 err(1, "socket");
317 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
319 int one = 1;
321 setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
322 sizeof(one));
324 #endif
325 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
326 if (keepalivep) {
327 int one = 1;
329 setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
330 sizeof(one));
332 #endif
334 if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
335 err(1, "connect(%s)", host);
336 xserver = connect_local_xsocket (0);
337 if (xserver < 0)
338 return 1;
339 return passive_session (xserver, fd, &key, schedule);
340 } else {
346 * Allocate a local pseudo-xserver and wait for connections
349 static int
350 doit_active (char *host, char *user,
351 int debugpp, int keepalivep, int tcpp, int port)
353 des_key_schedule schedule;
354 des_cblock key;
355 int otherside;
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);
360 void *ret;
361 u_int32_t tmp;
362 char *s;
364 otherside = connect_host (host, user, &key, schedule, port,
365 &me, &him);
366 if (otherside < 0)
367 return 1;
368 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
369 if (keepalivep) {
370 int one = 1;
372 setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
373 sizeof(one));
375 #endif
376 p = msg;
377 *p++ = INIT;
378 len = strlen(user);
379 p += krb_put_int (len, p, 4);
380 strncpy(p, user, len);
381 p += len;
382 *p++ = (keepalivep ? KEEP_ALIVE : 0);
384 s = getenv("DISPLAY");
385 if (s == NULL || (s = strchr(s, ':')) == NULL)
386 s = ":0";
387 len = strlen (s);
388 p += krb_put_int (len, p, 4);
389 strncpy (p, s, len);
390 p += len;
392 s = getenv("XAUTHORITY");
393 if (s == NULL)
394 s = "";
395 len = strlen (s);
396 p += krb_put_int (len, p, 4);
397 strncpy (p, s, len);
398 p += len;
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);
406 if (len < 0)
407 err (1, "read from %s", host);
408 p = (u_char *)ret;
409 if (*p == ERROR) {
410 p++;
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);
415 } else
416 p++;
418 tmp = get_xsockets (&rendez_vous1,
419 tcpp ? &rendez_vous2 : NULL);
420 if (tmp < 0)
421 return 1;
422 display_num = tmp;
423 if (tcpp)
424 snprintf (display, display_size, "localhost:%u", display_num);
425 else
426 snprintf (display, display_size, ":%u", display_num);
427 if (create_and_write_cookie (xauthfile, xauthfile_size, cookie, cookie_len))
428 return 1;
429 status_output (debugpp);
430 for (;;) {
431 fd_set fdset;
432 pid_t child;
433 int fd, thisfd;
434 int zero = 0;
436 FD_ZERO(&fdset);
437 if (rendez_vous1)
438 FD_SET(rendez_vous1, &fdset);
439 if (rendez_vous2)
440 FD_SET(rendez_vous2, &fdset);
441 if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0)
442 continue;
443 if (rendez_vous1 && FD_ISSET(rendez_vous1, &fdset))
444 thisfd = rendez_vous1;
445 else if (rendez_vous2 && FD_ISSET(rendez_vous2, &fdset))
446 thisfd = rendez_vous2;
447 else
448 continue;
450 fd = accept (thisfd, NULL, &zero);
451 if (fd < 0)
452 if (errno == EINTR)
453 continue;
454 else
455 err(1, "accept");
457 p = msg;
458 *p++ = NEW_CONN;
459 if (write_encrypted (otherside, msg, p - msg, schedule,
460 &key, &me, &him) < 0)
461 err (1, "write to %s", host);
462 len = read_encrypted (otherside, msg, sizeof(msg), &ret,
463 schedule, &key, &him, &me);
464 if (len < 0)
465 err (1, "read from %s", host);
466 p = (u_char *)ret;
467 if (*p == ERROR) {
468 p++;
469 p += krb_get_int (p, &tmp, 4, 0);
470 errx (1, "%s: %.*s", host, (int)tmp, p);
471 } else if (*p != NEW_CONN) {
472 errx (1, "%s: strange msg %d", host, *p);
473 } else {
474 p++;
475 p += krb_get_int (p, &tmp, 4, 0);
478 ++nchild;
479 child = fork ();
480 if (child < 0) {
481 warn("fork");
482 continue;
483 } else if (child == 0) {
484 int s;
485 struct sockaddr_in addr;
487 if (rendez_vous1)
488 close (rendez_vous1);
489 if (rendez_vous2)
490 close (rendez_vous2);
492 addr = him;
493 close (otherside);
495 addr.sin_port = htons(tmp);
496 s = socket (AF_INET, SOCK_STREAM, 0);
497 if (s < 0)
498 err(1, "socket");
499 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
501 int one = 1;
503 setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (void *)&one,
504 sizeof(one));
506 #endif
507 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
508 if (keepalivep) {
509 int one = 1;
511 setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
512 sizeof(one));
514 #endif
516 if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
517 err(1, "connect");
519 return active_session (fd, s, &key, schedule);
520 } else {
521 close (fd);
530 static int
531 check_for_passive (const char *disp)
533 char local_hostname[MaxHostNameLen];
535 gethostname (local_hostname, sizeof(local_hostname));
537 return disp != NULL &&
538 (*disp == ':'
539 || strncmp(disp, "unix", 4) == 0
540 || strncmp(disp, "localhost", 9) == 0
541 || strncmp(disp, local_hostname, strlen(local_hostname) == 0));
544 static void
545 usage(void)
547 fprintf (stderr, "Usage: %s [-p port] [-d] [-t] [-l remoteuser] host\n",
548 __progname);
549 exit (1);
553 * kx - forward x connection over a kerberos-encrypted channel.
558 main(int argc, char **argv)
560 int force_passive = 0;
561 int keepalivep = 1;
562 char *user = NULL;
563 int debugp = 0, tcpp = 0;
564 int c;
565 int port = 0;
567 set_progname (argv[0]);
568 while((c = getopt(argc, argv, "ktdl:p:P")) != EOF) {
569 switch(c) {
570 case 'd' :
571 debugp = 1;
572 break;
573 case 'k':
574 keepalivep = 0;
575 break;
576 case 't' :
577 tcpp = 1;
578 break;
579 case 'l' :
580 user = optarg;
581 break;
582 case 'p' :
583 port = htons(atoi (optarg));
584 break;
585 case 'P' :
586 force_passive = 1;
587 break;
588 case '?':
589 default:
590 usage();
594 argc -= optind;
595 argv += optind;
597 if (argc != 1)
598 usage ();
599 if (user == NULL) {
600 struct passwd *p = k_getpwuid (getuid ());
601 if (p == NULL)
602 errx(1, "Who are you?");
603 user = strdup (p->pw_name);
604 if (user == NULL)
605 errx (1, "strdup: out of memory");
607 if (port == 0)
608 port = k_getportbyname ("kx", "tcp", htons(KX_PORT));
609 signal (SIGCHLD, childhandler);
610 signal (SIGUSR1, usr1handler);
611 signal (SIGUSR2, usr2handler);
612 if (check_for_passive(getenv("DISPLAY")))
613 return doit_passive (argv[0], user, debugp, keepalivep, port);
614 else
615 return doit_active (argv[0], user, debugp, keepalivep, tcpp, port);