 785ad8784f
			
		
	
	785ad8784f
	
	
	
		
			
			git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@778 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			403 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "kx.h"
 | |
| 
 | |
| RCSID("$Id$");
 | |
| 
 | |
| char *prog;
 | |
| 
 | |
| static u_int32_t display_num;
 | |
| static char xauthfile[MaxPathLen];
 | |
| static int nchild;
 | |
| static int donep;
 | |
| 
 | |
| /*
 | |
|  * Signal handler that justs waits for the children when they die.
 | |
|  */
 | |
| 
 | |
| static RETSIGTYPE
 | |
| childhandler (int sig)
 | |
| {
 | |
|      pid_t pid;
 | |
|      int status;
 | |
| 
 | |
|      do { 
 | |
| 	 pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
 | |
| 	 if (pid > 0 && WIFEXITED(status) || WIFSIGNALED(status))
 | |
| 	     if (--nchild == 0 && donep)
 | |
| 		 exit (0);
 | |
|      } while(pid > 0);
 | |
|      signal (SIGCHLD, childhandler);
 | |
|      SIGRETURN(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handler for SIGUSR1.
 | |
|  * This signal means that we should wait until there are no children
 | |
|  * left and then exit.
 | |
|  */
 | |
| 
 | |
| static RETSIGTYPE
 | |
| usr1handler (int sig)
 | |
| {
 | |
|     donep = 1;
 | |
| 
 | |
|     SIGRETURN(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Establish authenticated connection
 | |
|  */
 | |
| 
 | |
| static int
 | |
| connect_host (char *host, des_cblock *key, des_key_schedule schedule,
 | |
| 	      int passivep)
 | |
| {
 | |
|      CREDENTIALS cred;
 | |
|      KTEXT_ST text;
 | |
|      MSG_DAT msg;
 | |
|      int status;
 | |
|      struct sockaddr_in thisaddr, thataddr;
 | |
|      int addrlen;
 | |
|      struct hostent *hostent;
 | |
|      int s;
 | |
|      u_char b;
 | |
|      char tmp[16];
 | |
| 
 | |
|      hostent = gethostbyname (host);
 | |
|      if (hostent == NULL) {
 | |
| 	  fprintf (stderr, "%s: gethostbyname '%s' failed: ", prog, host);
 | |
| 	  return -1;
 | |
|      }
 | |
| 
 | |
|      memset (&thataddr, 0, sizeof(thataddr));
 | |
|      thataddr.sin_family = AF_INET;
 | |
|      thataddr.sin_port   = k_getportbyname ("kx", "tcp", htons(KX_PORT));
 | |
|      memcpy (&thataddr.sin_addr, hostent->h_addr, sizeof(thataddr.sin_addr));
 | |
| 
 | |
|      s = socket (AF_INET, SOCK_STREAM, 0);
 | |
|      if (s < 0) {
 | |
| 	  fprintf (stderr, "%s: socket failed: %s\n", prog, strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      if (connect (s, (struct sockaddr *)&thataddr, sizeof(thataddr)) < 0) {
 | |
| 	  fprintf (stderr, "%s: connect(%s) failed: %s\n", prog, host,
 | |
| 		   strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      addrlen = sizeof(thisaddr);
 | |
|      if (getsockname (s, (struct sockaddr *)&thisaddr, &addrlen) < 0 ||
 | |
| 	 addrlen != sizeof(thisaddr)) {
 | |
| 	  fprintf (stderr, "%s: getsockname(%s) failed: %s\n",
 | |
| 		   prog, host, strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      status = krb_sendauth (KOPT_DO_MUTUAL, s, &text, "rcmd",
 | |
| 			    host, krb_realmofhost (host),
 | |
| 			    getpid(), &msg, &cred, schedule,
 | |
| 			    &thisaddr, &thataddr, "KXSERV.0");
 | |
|      if (status != KSUCCESS) {
 | |
| 	  fprintf (stderr, "%s: %s: %s\n", prog, host,
 | |
| 		   krb_get_err_text(status));
 | |
| 	  return -1;
 | |
|      }
 | |
|      if (krb_net_read (s, &b, sizeof(b)) != sizeof(b)) {
 | |
| 	  fprintf (stderr, "%s: read: %s\n", prog,
 | |
| 		   strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      if (b) {
 | |
| 	  char buf[BUFSIZ];
 | |
| 
 | |
| 	  krb_net_read (s, buf, sizeof(buf));
 | |
| 	  buf[BUFSIZ - 1] = '\0';
 | |
| 
 | |
| 	  fprintf (stderr, "%s: %s: %s\n", prog, host, buf);
 | |
| 	  return -1;
 | |
|      }
 | |
|      b = passivep;
 | |
|      if (krb_net_write (s, &b, sizeof(b)) != sizeof(b)) {
 | |
| 	  fprintf (stderr, "%s: write: %s\n", prog, strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      
 | |
|      if (krb_net_read (s, tmp, sizeof(tmp)) != sizeof(tmp)) {
 | |
| 	  fprintf (stderr, "%s: read %s\n", prog, strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
|      sscanf (tmp, "%u", &display_num);
 | |
|      if (krb_net_read (s, xauthfile, sizeof(xauthfile)) != sizeof(xauthfile)) {
 | |
| 	  fprintf (stderr, "%s: read: %s\n", prog,
 | |
| 		   strerror(errno));
 | |
| 	  return -1;
 | |
|      }
 | |
| 
 | |
|      memcpy(key, &cred.session, sizeof(des_cblock));
 | |
|      return s;
 | |
| }
 | |
| 
 | |
| static int
 | |
| active (int fd, char *host, des_cblock *iv, des_key_schedule schedule)
 | |
| {
 | |
|      int kxd;
 | |
|      u_char zero = 0;
 | |
| 
 | |
|      kxd = connect_host (host, iv, schedule, 0); /* XXX */
 | |
|      if (kxd < 0)
 | |
| 	  return 1;
 | |
|      if (krb_net_write (kxd, &zero, sizeof(zero)) != sizeof(zero)) {
 | |
| 	  fprintf (stderr, "%s: write: %s\n", prog,
 | |
| 		   strerror(errno));
 | |
| 	  return 1;
 | |
|      }
 | |
|      return copy_encrypted (fd, kxd, iv, schedule);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Get rid of the cookie that we were sent and get the correct one
 | |
|  * from our own cookie file instead.
 | |
|  */
 | |
| 
 | |
| static int
 | |
| start_session(int xserver, int fd, des_cblock *iv,
 | |
| 	      des_key_schedule schedule)
 | |
| {
 | |
|      u_char beg[12];
 | |
|      int bigendianp;
 | |
|      unsigned n, d, npad, dpad;
 | |
|      Xauth *auth;
 | |
|      FILE *f;
 | |
|      char *filename;
 | |
|      u_char zeros[6] = {0, 0, 0, 0, 0, 0};
 | |
| 
 | |
|      if (krb_net_read (fd, beg, sizeof(beg)) != sizeof(beg))
 | |
| 	  return 1;
 | |
|      if (krb_net_write (xserver, beg, 6) != 6)
 | |
| 	  return 1;
 | |
|      bigendianp = beg[0] == 'B';
 | |
|      if (bigendianp) {
 | |
| 	  n = (beg[6] << 8) | beg[7];
 | |
| 	  d = (beg[8] << 8) | beg[9];
 | |
|      } else {
 | |
| 	  n = (beg[7] << 8) | beg[6];
 | |
| 	  d = (beg[9] << 8) | beg[8];
 | |
|      }
 | |
|      if (n != 0 || d != 0)
 | |
| 	  return 1;
 | |
|      filename = XauFileName();
 | |
|      if (filename == NULL)
 | |
| 	  return 1;
 | |
|      f = fopen(filename, "r");
 | |
|      if (f) {
 | |
| 	  u_char len[6] = {0, 0, 0, 0, 0, 0};
 | |
| 
 | |
| 	  auth = XauReadAuth(f);
 | |
| 	  fclose(f);
 | |
| 	  n = auth->name_length;
 | |
| 	  d = auth->data_length;
 | |
| 	  if (bigendianp) {
 | |
| 	       len[0] = n >> 8;
 | |
| 	       len[1] = n & 0xFF;
 | |
| 	       len[2] = d >> 8;
 | |
| 	       len[3] = d & 0xFF;
 | |
| 	  } else {
 | |
| 	       len[0] = n & 0xFF;
 | |
| 	       len[1] = n >> 8;
 | |
| 	       len[2] = d & 0xFF;
 | |
| 	       len[3] = d >> 8;
 | |
| 	  }
 | |
| 	  if (krb_net_write (xserver, len, 6) != 6)
 | |
| 	       return 1;
 | |
| 	  if(krb_net_write (xserver, auth->name, n) != n)
 | |
| 	       return 1;
 | |
| 	  npad = (4 - (n % 4)) % 4;
 | |
| 	  if (npad) { 
 | |
| 	       if (krb_net_write (xserver, zeros, npad) != npad)
 | |
| 		    return 1;
 | |
| 	  }
 | |
| 	  if (krb_net_write (xserver, auth->data, d) != d)
 | |
| 	       return 1;
 | |
| 	  dpad = (4 - (d % 4)) % 4;
 | |
| 	  if (dpad) { 
 | |
| 	       if (krb_net_write (xserver, zeros, dpad) != dpad)
 | |
| 		    return 1;
 | |
| 	  }
 | |
| 	  XauDisposeAuth(auth);
 | |
|      } else {
 | |
| 	  if(krb_net_write(xserver, zeros, 6) != 6)
 | |
| 	       return 1;
 | |
|      }
 | |
| 
 | |
|      return copy_encrypted (xserver, fd, iv, schedule);
 | |
| }
 | |
| 
 | |
| static int
 | |
| passive (int fd, char *host, des_cblock *iv, des_key_schedule schedule)
 | |
| {
 | |
|      int xserver;
 | |
| 
 | |
|      xserver = connect_local_xsocket (0);
 | |
|      if (xserver < 0)
 | |
| 	  return 1;
 | |
|      return start_session (xserver, fd, iv, schedule);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Connect to the given host.
 | |
|  * Iff passivep, give it a port number to call you back and then wait.
 | |
|  * Else, listen on a local display and then connect to the remote host
 | |
|  * when a local client gets connected.
 | |
|  */
 | |
| 
 | |
| static int
 | |
| doit (char *host, int passivep, int debugp)
 | |
| {
 | |
|      des_key_schedule schedule;
 | |
|      des_cblock key;
 | |
|      int rendez_vous;
 | |
|      int (*fn)(int fd, char *host, des_cblock *iv,
 | |
| 	       des_key_schedule schedule);
 | |
| 
 | |
|      if (passivep) {
 | |
| 	  struct sockaddr_in newaddr;
 | |
| 	  int addrlen;
 | |
| 	  u_char b = passivep;
 | |
| 	  int otherside;
 | |
| 	  pid_t pid;
 | |
| 
 | |
| 	  otherside = connect_host (host, &key, schedule, passivep);
 | |
| 	  if (otherside < 0)
 | |
| 	       return 1;
 | |
| 
 | |
| 	  rendez_vous = socket (AF_INET, SOCK_STREAM, 0);
 | |
| 	  if (rendez_vous < 0) {
 | |
| 	       fprintf (stderr, "%s: socket failed: %s\n", prog,
 | |
| 			strerror(errno));
 | |
| 	       return 1;
 | |
| 	  }
 | |
| 	  memset (&newaddr, 0, sizeof(newaddr));
 | |
| 	  if (bind (rendez_vous, (struct sockaddr *)&newaddr,
 | |
| 		    sizeof(newaddr)) < 0) {
 | |
| 	       fprintf (stderr, "%s: bind: %s\n", prog, strerror(errno));
 | |
| 	       return 1;
 | |
| 	  }
 | |
| 	  addrlen = sizeof(newaddr);
 | |
| 	  if (getsockname (rendez_vous, (struct sockaddr *)&newaddr,
 | |
| 			   &addrlen) < 0) {
 | |
| 	       fprintf (stderr, "%s: getsockname: %s\n", prog,
 | |
| 			strerror(errno));
 | |
| 	       return 1;
 | |
| 	  }
 | |
| 	  if (listen (rendez_vous, SOMAXCONN) < 0) {
 | |
| 	       fprintf (stderr, "%s: listen: %s\n", prog, strerror(errno));
 | |
| 	       return 1;
 | |
| 	  }
 | |
| 	  {
 | |
| 	      char tmp[6];
 | |
| 
 | |
| 	      sprintf (tmp, "%d", ntohs(newaddr.sin_port));
 | |
| 	      if (krb_net_write (otherside, tmp, sizeof(tmp))
 | |
| 		  != sizeof(tmp)) {
 | |
| 		  fprintf (stderr, "%s: write: %s\n", prog, strerror(errno));
 | |
| 		  return 1;
 | |
| 	      }
 | |
| 	  }
 | |
| 	  /* close (otherside); */
 | |
| 	  fn = passive;
 | |
| 	  if(debugp)
 | |
| 	       printf ("%d\t%d\t%s\n", getpid(), display_num, xauthfile);
 | |
| 	  else {
 | |
| 	      pid = fork();
 | |
| 	      if (pid < 0) {
 | |
| 		  fprintf (stderr, "%s: fork: %s\n", prog, strerror(errno));
 | |
| 		  return 1;
 | |
| 	      } else if (pid > 0) {
 | |
| 		  printf ("%d\t%d\t%s\n", pid, display_num, xauthfile);
 | |
| 		  exit (0);
 | |
| 	      } else {
 | |
| 		  fclose(stdout);
 | |
| 	      }
 | |
| 	  }
 | |
|      } else {
 | |
| 	  display_num = get_xsockets (&rendez_vous, NULL);
 | |
| 	  if (display_num < 0)
 | |
| 	       return 1;
 | |
| 	  fn = active;
 | |
|      }
 | |
|      for (;;) {
 | |
| 	  pid_t child;
 | |
| 	  int fd;
 | |
| 	  int zero = 0;
 | |
| 
 | |
| 	  fd = accept (rendez_vous, NULL, &zero);
 | |
| 	  if (fd < 0)
 | |
| 	       if (errno == EINTR)
 | |
| 		    continue;
 | |
| 	       else {
 | |
| 		    fprintf (stderr, "%s: accept: %s\n", prog,
 | |
| 			     strerror(errno));
 | |
| 		    return 1;
 | |
| 	       }
 | |
| 	  ++nchild;
 | |
| 	  child = fork ();
 | |
| 	  if (child < 0) {
 | |
| 	       fprintf (stderr, "%s: fork: %s\n", prog,
 | |
| 			strerror(errno));
 | |
| 	       continue;
 | |
| 	  } else if (child == 0) {
 | |
| 	       close (rendez_vous);
 | |
| 	       return (*fn)(fd, host, &key, schedule);
 | |
| 	  } else {
 | |
| 	       close (fd);
 | |
| 	  }
 | |
|      }
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage(void)
 | |
| {
 | |
|     fprintf (stderr, "Usage: %s [-d] host\n", prog);
 | |
|     exit (1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * kx - forward x connection over a kerberos-encrypted channel.
 | |
|  *
 | |
|  * passive mode if $DISPLAY begins with :
 | |
|  */
 | |
| 
 | |
| int
 | |
| main(int argc, char **argv)
 | |
| {
 | |
|      int passivep;
 | |
|      char *disp;
 | |
|      int debugp = 0;
 | |
|      int c;
 | |
| 
 | |
|      prog = argv[0];
 | |
|      while((c = getopt(argc, argv, "d")) != EOF) {
 | |
| 	 switch(c) {
 | |
| 	 case 'd' :
 | |
| 	     debugp = 1;
 | |
| 	     break;
 | |
| 	 case '?':
 | |
| 	 default:
 | |
| 	     usage();
 | |
| 	 }
 | |
|      }
 | |
| 
 | |
|      argc -= optind;
 | |
|      argv += optind;
 | |
| 
 | |
|      if (argc != 1)
 | |
| 	  usage ();
 | |
|      disp = getenv("DISPLAY");
 | |
|      passivep = disp != NULL && 
 | |
|        (*disp == ':' || strncmp(disp, "unix", 4) == 0);
 | |
|      /* non passive mode has not been tested and I'm not sure it works
 | |
|       *	at all, so better disable it.
 | |
|       */
 | |
|      passivep = 1;
 | |
|      signal (SIGCHLD, childhandler);
 | |
|      signal (SIGUSR1, usr1handler);
 | |
|      return doit (argv[0], passivep, debugp);
 | |
| }
 |