diff --git a/appl/kx/Makefile.in b/appl/kx/Makefile.in index 493bd9842..d82b2c466 100644 --- a/appl/kx/Makefile.in +++ b/appl/kx/Makefile.in @@ -24,12 +24,13 @@ PROGS = $(PROG_BIN) $(PROG_LIBEXEC) SOURCES_KX = kx.c SOURCES_KXD = kxd.c +SOURCES_COMMON = common.c -OBJECTS_KX = kx.o -OBJECTS_KXD = kxd.o +OBJECTS_KX = kx.o common.o +OBJECTS_KXD = kxd.o common.o OBJECTS = $(OBJECTS_KX) $(OBJECTS_KXD) -SOURCES = $(SOURCES_KX) $(SOURCES_KXD) +SOURCES = $(SOURCES_KX) $(SOURCES_KXD) $(SOURCES_COMMON) all: $(PROGS) diff --git a/appl/kx/kx.c b/appl/kx/kx.c index 4ba40fd3e..3cc190546 100644 --- a/appl/kx/kx.c +++ b/appl/kx/kx.c @@ -2,253 +2,226 @@ RCSID("$Id$"); -static char *prog; +char *prog; static void usage() { - fprintf (stderr, "Usage: %s local-display-number host|display\n", - prog); - exit (1); -} - -static int -doit_host (char *host, unsigned dnr, int fd) -{ - CREDENTIALS cred; - KTEXT_ST text; - MSG_DAT msg; - int status; - des_key_schedule schedule; - struct sockaddr_in thisaddr, thataddr; - int addrlen; - void *ret; - struct hostent *hostent; - int s; - struct sockaddr_un unixaddr; - des_cblock iv1, iv2; - int num1 = 0, num2 = 0; - - /* - * Establish authenticated connection - */ - - 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(2111)); - 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, k_strerror(errno)); - return 1; - } - if (connect (s, (struct sockaddr *)&thataddr, sizeof(thataddr)) < 0) { - fprintf (stderr, "%s: connect(%s) failed: %s\n", prog, host, - k_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, k_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; - } - /* - * Send parameters. - */ - - { - u_char b = dnr; - char buf[128]; - int ret; - - if (write (s, &b, sizeof(b)) != sizeof(b)) { - fprintf (stderr, "%s: write: %s\n", prog, k_strerror (errno)); - return 1; - } - if (read (s, &b, sizeof(b)) != sizeof(b)) { - fprintf (stderr, "%s: read: %s\n", prog, k_strerror (errno)); - return 1; - } - if (b) { - fprintf (stderr, "%s: Error from '%s': ", prog, host); - ret = read (s, buf, sizeof(buf)); - if(ret < 0) { - fprintf (stderr, "%s: read: %s\n", prog, k_strerror (errno)); - return 1; - } - fprintf (stderr, "%s\n", buf); - return 1; - } - } - - memcpy (&iv1, &cred.session, sizeof(iv1)); - memcpy (&iv2, &cred.session, sizeof(iv2)); - for (;;) { - fd_set fdset; - int ret; - char buf[BUFSIZ]; - - FD_ZERO(&fdset); - FD_SET(s, &fdset); - FD_SET(fd, &fdset); - - ret = select (256, &fdset, NULL, NULL, NULL); /* XXX */ - if (ret < 0 && errno != EINTR) { - fprintf (stderr, "%s: select: %s\n", prog, k_strerror (errno)); - return 1; - } - if (FD_ISSET(s, &fdset)) { - ret = read (s, buf, sizeof(buf)); - if (ret == 0) - return 0; - if (ret < 0) { - fprintf (stderr, "%s: read: %s\n", prog, k_strerror (errno)); - return 1; - } -#ifndef NOENCRYPTION - des_cfb64_encrypt (buf, buf, ret, schedule, &iv1, - &num1, DES_DECRYPT); -#endif - ret = krb_net_write (fd, buf, ret); - if (ret < 0) { - fprintf (stderr, "%s: write: %s\n", prog, k_strerror (errno)); - return 1; - } - } - if (FD_ISSET(fd, &fdset)) { - ret = read (fd, buf, sizeof(buf)); - if (ret == 0) - return 0; - if (ret < 0) { - fprintf (stderr, "%s: read: %s\n", prog, k_strerror (errno)); - return 1; - } -#ifndef NOENCRYPTION - des_cfb64_encrypt (buf, buf, ret, schedule, &iv2, - &num2, DES_ENCRYPT); -#endif - ret = krb_net_write (s, buf, ret); - if (ret < 0) { - fprintf (stderr, "%s: write: %s\n", prog, k_strerror (errno)); - return 1; - } - } - } + fprintf (stderr, "Usage: %s host\n", + prog); + exit (1); } /* - * Listen for calls to the remote X-server. + * Establish authenticated connection */ static int -doit (unsigned localnr, char *host, unsigned remotenr) +connect_host (char *host, des_cblock *key, des_key_schedule schedule) { - int fd; - struct sockaddr_un addr; - int dnr; + 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; - fd = socket (AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - fprintf (stderr, "%s: socket failed: %s\n", prog, k_strerror(errno)); - return 1; - } - addr.sun_family = AF_UNIX; - sprintf (addr.sun_path, "/tmp/.X11-unix/X%u", localnr); - unlink (addr.sun_path); - if(bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf (stderr, "%s: bind failed: %s\n", prog, - k_strerror(errno)); - return 1; - } - if (listen (fd, 5) < 0) { - fprintf (stderr, "%s: listen failed: %s\n", prog, - k_strerror(errno)); - return 1; - } - for (;;) { - int newfd; - struct sockaddr_un that; - int len; - pid_t pid; + hostent = gethostbyname (host); + if (hostent == NULL) { + fprintf (stderr, "%s: gethostbyname '%s' failed: ", prog, host); + return -1; + } - len = sizeof (that); - newfd = accept (fd, (struct sockaddr *)&that, &len); - if (newfd < 0) - if (errno == EINTR) - continue; - else { - fprintf (stderr, "%s: accept: %s\n", prog, k_strerror (errno)); - return 1; - } - fprintf (stderr, "%s: New connection\n", prog); - pid = fork (); - if (pid < 0) { - fprintf (stderr, "%s: fork: %s\n", prog, k_strerror (errno)); - return 1; - } else if (pid == 0) { - close (fd); - return doit_host (host, remotenr, newfd); - } else { - close (newfd); - } - } + memset (&thataddr, 0, sizeof(thataddr)); + thataddr.sin_family = AF_INET; + thataddr.sin_port = k_getportbyname ("kx", "tcp", htons(2111)); + 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, k_strerror(errno)); + return -1; + } + if (connect (s, (struct sockaddr *)&thataddr, sizeof(thataddr)) < 0) { + fprintf (stderr, "%s: connect(%s) failed: %s\n", prog, host, + k_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, k_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 (read (s, &b, sizeof(b)) != sizeof(b)) { + fprintf (stderr, "%s: read: %s\n", prog, + k_strerror(errno)); + return -1; + } + if (b) { + char buf[BUFSIZ]; + + read (s, buf, sizeof(buf)); + buf[BUFSIZ - 1] = '\0'; + + fprintf (stderr, "%s: %s: %s\n", prog, host, buf); + return -1; + } + + memcpy(key, &cred.session, sizeof(des_cblock)); + return s; } -static -RETSIGTYPE -childhandler () +static int +active (int fd, char *host, des_cblock *iv, des_key_schedule schedule) { - pid_t pid; - int status; + int kxd; + u_char zero = 0; - do { - pid = waitpid (-1, &status, WNOHANG|WUNTRACED); - } while(pid > 0); - signal (SIGCHLD, childhandler); + kxd = connect_host (host, iv, schedule); + if (kxd < 0) + return 1; + if (write (kxd, &zero, sizeof(zero)) != sizeof(zero)) { + fprintf (stderr, "%s: write: %s\n", prog, + k_strerror(errno)); + return 1; + } + return copy_encrypted (fd, kxd, 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 copy_encrypted (xserver, fd, iv, schedule); } /* - * fx - forward x connection. + * 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 otherside; + 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; + + otherside = connect_host (host, &key, schedule); + 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, + k_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, k_strerror(errno)); + return 1; + } + addrlen = sizeof(newaddr); + if (getsockname (rendez_vous, (struct sockaddr *)&newaddr, + &addrlen) < 0) { + fprintf (stderr, "%s: getsockname: %s\n", prog, + k_strerror(errno)); + return 1; + } + if (listen (rendez_vous, SOMAXCONN) < 0) { + fprintf (stderr, "%s: listen: %s\n", prog, k_strerror(errno)); + return 1; + } + if (write (otherside, &b, sizeof(b)) != sizeof(b) || + write (otherside, &newaddr.sin_port, sizeof(newaddr.sin_port)) + != sizeof(newaddr.sin_port)) { + fprintf (stderr, "%s: write: %s\n", prog, k_strerror(errno)); + return 1; + } + close (otherside); + fn = passive; + } else { + rendez_vous = get_local_xsocket (1); /* XXX */ + if (rendez_vous < 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, + k_strerror(errno)); + return 1; + } + child = fork (); + if (child < 0) { + fprintf (stderr, "%s: fork: %s\n", prog, + k_strerror(errno)); + continue; + } else if (child == 0) { + close (rendez_vous); + return (*fn)(fd, host, &key, schedule); + } else { + close (fd); + } + } +} + +/* + * kx - forward x connection over a kerberos-encrypted channel. + * + * passive mode if $DISPLAY begins with : */ int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { - int dnr; - char *p; + int passivep; + char *disp; - prog = argv[0]; - if (argc != 3) - usage (); - - p = strchr (argv[2], ':'); - if (p) { - *p = '\0'; - dnr = atoi (p+1); - } else - dnr = 0; - - signal (SIGCHLD, childhandler); - - return doit (atoi(argv[1]), argv[2], dnr); + prog = argv[0]; + if (argc != 2) + usage (); + disp = getenv("DISPLAY"); + passivep = disp != NULL && *disp == ':'; + signal (SIGCHLD, childhandler); + return doit (argv[1], passivep); } diff --git a/appl/kx/kx.h b/appl/kx/kx.h index 47a09f110..41b4d65ee 100644 --- a/appl/kx/kx.h +++ b/appl/kx/kx.h @@ -24,3 +24,21 @@ #include #include + +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + +extern char *prog; + +int copy_encrypted (int fd1, int fd2, des_cblock *iv, + des_key_schedule schedule); + +RETSIGTYPE childhandler (); + +int get_local_xsocket (unsigned dnr); +int connect_local_xsocket (unsigned dnr); diff --git a/appl/kx/kxd.c b/appl/kx/kxd.c index 41f8bf729..04560b954 100644 --- a/appl/kx/kxd.c +++ b/appl/kx/kxd.c @@ -4,6 +4,8 @@ RCSID("$Id$"); #include +char *prog; + static int fatal (int fd, char *s) { @@ -16,27 +18,19 @@ fatal (int fd, char *s) } static int -doit(int sock) +recv_conn (int sock, des_cblock *key, des_key_schedule schedule, + struct sockaddr_in *retaddr) { int status; KTEXT_ST ticket; AUTH_DAT auth; char instance[INST_SZ + 1]; - des_key_schedule schedule; struct sockaddr_in thisaddr, thataddr; int addrlen; - int len; - char buf[BUFSIZ]; - void *data; - struct passwd *passwd; char version[KRB_SENDAUTH_VLEN]; - int fd; - struct stat sb; - struct sockaddr_un addr; char *username; - des_cblock iv1, iv2; - int num1 = 0, num2 = 0; - unsigned dnr; + u_char ok = 0; + struct passwd *passwd; addrlen = sizeof(thisaddr); if (getsockname (sock, (struct sockaddr *)&thisaddr, &addrlen) < 0 || @@ -57,17 +51,7 @@ doit(int sock) strncmp(version, "KXSERV.0", KRB_SENDAUTH_VLEN) != 0) { return 1; } - { - u_char b; - - if (read (sock, &b, sizeof(b)) != sizeof(b)) - return 1; - dnr = b; - } - - if (stat ("/dev/console", &sb) < 0) - return fatal (sock, "Cannot stat /dev/console"); - passwd = getpwuid (sb.st_uid); + passwd = getpwnam (auth.pname); if (passwd == NULL) return fatal (sock, "Cannot find uid"); username = strdup (passwd->pw_name); @@ -79,65 +63,103 @@ doit(int sock) setuid(passwd->pw_uid)) { return fatal (sock, "Cannot set uid"); } - fd = socket (AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return fatal (sock, "Cannot create socket"); - addr.sun_family = AF_UNIX; - sprintf (addr.sun_path, "/tmp/.X11-unix/X%u", dnr); - if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) - { - char msg[200]; - sprintf (msg, "Cannot connect to %s", addr.sun_path); - return fatal (sock, msg); - } - memcpy (&iv1, &auth.session, sizeof(iv1)); - memcpy (&iv2, &auth.session, sizeof(iv2)); - { - u_char b = 0; + if (write (sock, &ok, sizeof(ok)) != sizeof(ok)) + return 1; - if (write (sock, &b, sizeof(b)) != sizeof(b)) - return 1; - } - for (;;) { - fd_set fdset; - int ret; - char buf[BUFSIZ]; + memcpy(key, &auth.session, sizeof(des_cblock)); + *retaddr = thataddr; + return 0; +} - FD_ZERO(&fdset); - FD_SET(sock, &fdset); - FD_SET(fd, &fdset); +static int +doit_conn (int fd, struct sockaddr_in *thataddr, + des_cblock *key, des_key_schedule schedule) +{ + int sock; - ret = select (fd+1, &fdset, NULL, NULL, NULL); - if (ret < 0 && errno != EINTR) + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + char msg[200]; + sprintf (msg, "socket: %s", k_strerror(errno)); + return fatal (sock, msg); + } + if (connect (sock, (struct sockaddr *)thataddr, + sizeof(*thataddr)) < 0) { + abort (); + } + return copy_encrypted (fd, sock, key, schedule); +} + +/* + * + */ + +static int +check_user_console () +{ + struct stat sb; + + if (stat ("/dev/console", &sb) < 0) + return fatal (0, "Cannot stat /dev/console"); + if (getuid() != sb.st_uid) + return fatal (0, "Permission denied"); + return 0; +} + +static int +doit(int sock) +{ + u_char passivep; + struct sockaddr_in thataddr; + des_key_schedule schedule; + des_cblock key; + int localx; + + if (recv_conn (sock, &key, schedule, &thataddr)) + return 1; + if (read (sock, &passivep, sizeof(passivep)) != sizeof(passivep)) + return 1; + if (passivep) { + if (read (sock, &thataddr.sin_port, sizeof(thataddr.sin_port)) + != sizeof(thataddr.sin_port)) return 1; - if (FD_ISSET(sock, &fdset)) { - ret = read (sock, buf, sizeof(buf)); - if (ret == 0) - return 0; - if (ret < 0) - return 1; -#ifndef NOENCRYPTION - des_cfb64_encrypt (buf, buf, ret, schedule, - &iv1, &num1, DES_DECRYPT); -#endif - ret = krb_net_write (fd, buf, ret); - if (ret < 0) - return 1; - } - if (FD_ISSET(fd, &fdset)) { - ret = read (fd, buf, sizeof(buf)); - if (ret == 0) - return 0; - if (ret < 0) - return 1; -#ifndef NOENCRYPTION - des_cfb64_encrypt (buf, buf, ret, schedule, - &iv2, &num2, DES_ENCRYPT); -#endif - ret = krb_net_write (STDOUT_FILENO, buf, ret); - if (ret < 0) - return 1; + localx = get_local_xsocket (1); + if (localx < 0) + return 1; + for (;;) { + pid_t child; + int fd; + int zero = 0; + + fd = accept (localx, NULL, &zero); + if (fd < 0) + if (errno == EINTR) + continue; + else { + char msg[200]; + sprintf (msg, "accept: %s\n", k_strerror (errno)); + return fatal (sock, msg); + } + child = fork (); + if (child < 0) { + char msg[200]; + sprintf (msg, "fork: %s\n", k_strerror (errno)); + return fatal(sock, msg); + } else if (child == 0) { + close (localx); + return doit_conn (fd, &thataddr, &key, schedule); + } else { + close (fd); + } } + } else { + if (check_user_console ()) + return 1; + + localx = connect_local_xsocket (0); + if (localx < 0) + return 1; + return copy_encrypted (localx, sock, &key, schedule); } } @@ -148,6 +170,9 @@ doit(int sock) int main (int argc, char **argv) { - openlog(argv[0], LOG_PID|LOG_CONS, LOG_DAEMON); + prog = argv[0]; + + openlog(prog, LOG_PID|LOG_CONS, LOG_DAEMON); + signal (SIGCHLD, childhandler); return doit(0); }