diff options
author | assar <assar@FreeBSD.org> | 2001-06-21 02:12:07 +0000 |
---|---|---|
committer | assar <assar@FreeBSD.org> | 2001-06-21 02:12:07 +0000 |
commit | 0c8fa354358381b3f1b92598e7f1b46f8cf744cc (patch) | |
tree | ed28ffb73cc0ae48a9892dab3f10b09bc36436d5 /crypto/heimdal/appl/kx/kx.c | |
parent | 06c859ecf534f468a52f24a3eb14409d73a4907c (diff) | |
download | FreeBSD-src-0c8fa354358381b3f1b92598e7f1b46f8cf744cc.zip FreeBSD-src-0c8fa354358381b3f1b92598e7f1b46f8cf744cc.tar.gz |
import of heimdal 0.3f
Diffstat (limited to 'crypto/heimdal/appl/kx/kx.c')
-rw-r--r-- | crypto/heimdal/appl/kx/kx.c | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/crypto/heimdal/appl/kx/kx.c b/crypto/heimdal/appl/kx/kx.c new file mode 100644 index 0000000..63e1595 --- /dev/null +++ b/crypto/heimdal/appl/kx/kx.c @@ -0,0 +1,765 @@ +/* + * Copyright (c) 1995 - 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kx.h" + +RCSID("$Id: kx.c,v 1.68 2001/02/20 01:44:45 assar Exp $"); + +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); +} + +/* + * Almost the same as for SIGUSR1, except we should exit immediately + * if there are no active children. + */ + +static RETSIGTYPE +usr2handler (int sig) +{ + donep = 1; + if (nchild == 0) + exit (0); + + SIGRETURN(0); +} + +/* + * Establish authenticated connection. Return socket or -1. + */ + +static int +connect_host (kx_context *kc) +{ + struct addrinfo *ai, *a; + struct addrinfo hints; + int error; + char portstr[NI_MAXSERV]; + socklen_t addrlen; + int s; + struct sockaddr_storage thisaddr_ss; + struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss; + + memset (&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + snprintf (portstr, sizeof(portstr), "%u", ntohs(kc->port)); + + error = getaddrinfo (kc->host, portstr, &hints, &ai); + if (error) { + warnx ("%s: %s", kc->host, gai_strerror(error)); + return -1; + } + + for (a = ai; a != NULL; a = a->ai_next) { + s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); + if (s < 0) + continue; + if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { + warn ("connect(%s)", kc->host); + close (s); + continue; + } + break; + } + + if (a == NULL) { + freeaddrinfo (ai); + return -1; + } + + addrlen = a->ai_addrlen; + if (getsockname (s, thisaddr, &addrlen) < 0 || + addrlen != a->ai_addrlen) + err(1, "getsockname(%s)", kc->host); + memcpy (&kc->thisaddr, thisaddr, sizeof(kc->thisaddr)); + memcpy (&kc->thataddr, a->ai_addr, sizeof(kc->thataddr)); + freeaddrinfo (ai); + if ((*kc->authenticate)(kc, s)) + return -1; + return s; +} + +/* + * Get rid of the cookie that we were sent and get the correct one + * from our own cookie file instead and then just copy data in both + * directions. + */ + +static int +passive_session (int xserver, int fd, kx_context *kc) +{ + if (replace_cookie (xserver, fd, XauFileName(), 1)) + return 1; + else + return copy_encrypted (kc, xserver, fd); +} + +static int +active_session (int xserver, int fd, kx_context *kc) +{ + if (verify_and_remove_cookies (xserver, fd, 1)) + return 1; + else + return copy_encrypted (kc, xserver, fd); +} + +/* + * fork (unless debugp) and print the output that will be used by the + * script to capture the display, xauth cookie and pid. + */ + +static void +status_output (int debugp) +{ + if(debugp) + printf ("%u\t%s\t%s\n", (unsigned)getpid(), display, xauthfile); + else { + pid_t pid; + + pid = fork(); + if (pid < 0) { + err(1, "fork"); + } else if (pid > 0) { + printf ("%u\t%s\t%s\n", (unsigned)pid, display, xauthfile); + exit (0); + } else { + fclose(stdout); + } + } +} + +/* + * Obtain an authenticated connection on `kc'. Send a kx message + * saying we are `kc->user' and want to use passive mode. Wait for + * answer on that connection and fork of a child for every new + * connection we have to make. + */ + +static int +doit_passive (kx_context *kc) +{ + int otherside; + u_char msg[1024], *p; + int len; + u_int32_t tmp; + const char *host = kc->host; + + otherside = connect_host (kc); + + if (otherside < 0) + return 1; +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (kc->keepalive_flag) { + int one = 1; + + setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + + p = msg; + *p++ = INIT; + len = strlen(kc->user); + p += KRB_PUT_INT (len, p, sizeof(msg) - 1, 4); + memcpy(p, kc->user, len); + p += len; + *p++ = PASSIVE | (kc->keepalive_flag ? KEEP_ALIVE : 0); + if (kx_write (kc, otherside, msg, p - msg) != p - msg) + err (1, "write to %s", host); + len = kx_read (kc, otherside, msg, sizeof(msg)); + if (len <= 0) + errx (1, + "error reading initial message from %s: " + "this probably means it's using an old version.", + host); + p = (u_char *)msg; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %.*s", host, (int)tmp, p); + } else if (*p != ACK) { + errx (1, "%s: strange msg %d", host, *p); + } else + p++; + p += krb_get_int (p, &tmp, 4, 0); + memcpy(display, p, tmp); + display[tmp] = '\0'; + p += tmp; + + p += krb_get_int (p, &tmp, 4, 0); + memcpy(xauthfile, p, tmp); + xauthfile[tmp] = '\0'; + p += tmp; + + status_output (kc->debug_flag); + for (;;) { + pid_t child; + + len = kx_read (kc, otherside, msg, sizeof(msg)); + if (len < 0) + err (1, "read from %s", host); + else if (len == 0) + return 0; + + p = (u_char *)msg; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %.*s", host, (int)tmp, p); + } else if(*p != NEW_CONN) { + errx (1, "%s: strange msg %d", host, *p); + } else { + p++; + p += krb_get_int (p, &tmp, 4, 0); + } + + ++nchild; + child = fork (); + if (child < 0) { + warn("fork"); + continue; + } else if (child == 0) { + struct sockaddr_in addr; + int fd; + int xserver; + + addr = kc->thataddr; + close (otherside); + + addr.sin_port = htons(tmp); + fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd < 0) + err(1, "socket"); +#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) + { + int one = 1; + + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one, + sizeof(one)); + } +#endif +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (kc->keepalive_flag) { + int one = 1; + + setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + + if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + err(1, "connect(%s)", host); + { + int d = 0; + char *s; + + s = getenv ("DISPLAY"); + if (s != NULL) { + s = strchr (s, ':'); + if (s != NULL) + d = atoi (s + 1); + } + + xserver = connect_local_xsocket (d); + if (xserver < 0) + return 1; + } + return passive_session (xserver, fd, kc); + } else { + } + } +} + +/* + * Allocate a local pseudo-xserver and wait for connections + */ + +static int +doit_active (kx_context *kc) +{ + int otherside; + int nsockets; + struct x_socket *sockets; + u_char msg[1024], *p; + int len = strlen(kc->user); + int tmp, tmp2; + char *s; + int i; + size_t rem; + u_int32_t other_port; + int error; + const char *host = kc->host; + + otherside = connect_host (kc); + if (otherside < 0) + return 1; +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (kc->keepalive_flag) { + int one = 1; + + setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + p = msg; + rem = sizeof(msg); + *p++ = INIT; + --rem; + len = strlen(kc->user); + tmp = KRB_PUT_INT (len, p, rem, 4); + if (tmp < 0) + return 1; + p += tmp; + rem -= tmp; + memcpy(p, kc->user, len); + p += len; + rem -= len; + *p++ = (kc->keepalive_flag ? KEEP_ALIVE : 0); + --rem; + + s = getenv("DISPLAY"); + if (s == NULL || (s = strchr(s, ':')) == NULL) + s = ":0"; + len = strlen (s); + tmp = KRB_PUT_INT (len, p, rem, 4); + if (tmp < 0) + return 1; + rem -= tmp; + p += tmp; + memcpy (p, s, len); + p += len; + rem -= len; + + s = getenv("XAUTHORITY"); + if (s == NULL) + s = ""; + len = strlen (s); + tmp = KRB_PUT_INT (len, p, rem, 4); + if (tmp < 0) + return 1; + p += len; + rem -= len; + memcpy (p, s, len); + p += len; + rem -= len; + + if (kx_write (kc, otherside, msg, p - msg) != p - msg) + err (1, "write to %s", host); + + len = kx_read (kc, otherside, msg, sizeof(msg)); + if (len < 0) + err (1, "read from %s", host); + p = (u_char *)msg; + if (*p == ERROR) { + u_int32_t u32; + + p++; + p += krb_get_int (p, &u32, 4, 0); + errx (1, "%s: %.*s", host, (int)u32, p); + } else if (*p != ACK) { + errx (1, "%s: strange msg %d", host, *p); + } else + p++; + + tmp2 = get_xsockets (&nsockets, &sockets, kc->tcp_flag); + if (tmp2 < 0) + return 1; + display_num = tmp2; + if (kc->tcp_flag) + snprintf (display, display_size, "localhost:%u", display_num); + else + snprintf (display, display_size, ":%u", display_num); + error = create_and_write_cookie (xauthfile, xauthfile_size, + cookie, cookie_len); + if (error) { + warnx ("failed creating cookie file: %s", strerror(error)); + return 1; + } + status_output (kc->debug_flag); + for (;;) { + fd_set fdset; + pid_t child; + int fd, thisfd = -1; + socklen_t zero = 0; + + FD_ZERO(&fdset); + for (i = 0; i < nsockets; ++i) { + if (sockets[i].fd >= FD_SETSIZE) + errx (1, "fd too large"); + FD_SET(sockets[i].fd, &fdset); + } + if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0) + continue; + for (i = 0; i < nsockets; ++i) + if (FD_ISSET(sockets[i].fd, &fdset)) { + thisfd = sockets[i].fd; + break; + } + fd = accept (thisfd, NULL, &zero); + if (fd < 0) { + if (errno == EINTR) + continue; + else + err(1, "accept"); + } + + p = msg; + *p++ = NEW_CONN; + if (kx_write (kc, otherside, msg, p - msg) != p - msg) + err (1, "write to %s", host); + len = kx_read (kc, otherside, msg, sizeof(msg)); + if (len < 0) + err (1, "read from %s", host); + p = (u_char *)msg; + if (*p == ERROR) { + u_int32_t val; + + p++; + p += krb_get_int (p, &val, 4, 0); + errx (1, "%s: %.*s", host, (int)val, p); + } else if (*p != NEW_CONN) { + errx (1, "%s: strange msg %d", host, *p); + } else { + p++; + p += krb_get_int (p, &other_port, 4, 0); + } + + ++nchild; + child = fork (); + if (child < 0) { + warn("fork"); + continue; + } else if (child == 0) { + int s; + struct sockaddr_in addr; + + for (i = 0; i < nsockets; ++i) + close (sockets[i].fd); + + addr = kc->thataddr; + close (otherside); + + addr.sin_port = htons(other_port); + s = socket (AF_INET, SOCK_STREAM, 0); + if (s < 0) + err(1, "socket"); +#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) + { + int one = 1; + + setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (void *)&one, + sizeof(one)); + } +#endif +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (kc->keepalive_flag) { + int one = 1; + + setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + + if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) < 0) + err(1, "connect"); + + return active_session (fd, s, kc); + } else { + close (fd); + } + } +} + +/* + * Should we interpret `disp' as this being a passive call? + */ + +static int +check_for_passive (const char *disp) +{ + char local_hostname[MaxHostNameLen]; + + gethostname (local_hostname, sizeof(local_hostname)); + + return disp != NULL && + (*disp == ':' + || strncmp(disp, "unix", 4) == 0 + || strncmp(disp, "localhost", 9) == 0 + || strncmp(disp, local_hostname, strlen(local_hostname)) == 0); +} + +/* + * Set up signal handlers and then call the functions. + */ + +static int +doit (kx_context *kc, int passive_flag) +{ + signal (SIGCHLD, childhandler); + signal (SIGUSR1, usr1handler); + signal (SIGUSR2, usr2handler); + if (passive_flag) + return doit_passive (kc); + else + return doit_active (kc); +} + +#ifdef KRB4 + +/* + * Start a v4-authenticatated kx connection. + */ + +static int +doit_v4 (const char *host, int port, const char *user, + int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag) +{ + int ret; + kx_context context; + + krb4_make_context (&context); + context_set (&context, + host, user, port, debug_flag, keepalive_flag, tcp_flag); + + ret = doit (&context, passive_flag); + context_destroy (&context); + return ret; +} +#endif /* KRB4 */ + +#ifdef KRB5 + +/* + * Start a v5-authenticatated kx connection. + */ + +static int +doit_v5 (const char *host, int port, const char *user, + int passive_flag, int debug_flag, int keepalive_flag, int tcp_flag) +{ + int ret; + kx_context context; + + krb5_make_context (&context); + context_set (&context, + host, user, port, debug_flag, keepalive_flag, tcp_flag); + + ret = doit (&context, passive_flag); + context_destroy (&context); + return ret; +} +#endif /* KRB5 */ + +/* + * Variables set from the arguments + */ + +#ifdef KRB4 +static int use_v4 = -1; +#ifdef HAVE_KRB_ENABLE_DEBUG +static int krb_debug_flag = 0; +#endif /* HAVE_KRB_ENABLE_DEBUG */ +#endif /* KRB4 */ +#ifdef KRB5 +static int use_v5 = -1; +#endif +static char *port_str = NULL; +static const char *user = NULL; +static int tcp_flag = 0; +static int passive_flag = 0; +static int keepalive_flag = 1; +static int debug_flag = 0; +static int version_flag = 0; +static int help_flag = 0; + +struct getargs args[] = { +#ifdef KRB4 + { "krb4", '4', arg_flag, &use_v4, "Use Kerberos V4", + NULL }, +#ifdef HAVE_KRB_ENABLE_DEBUG + { "krb4-debug", 'D', arg_flag, &krb_debug_flag, + "enable krb4 debugging" }, +#endif /* HAVE_KRB_ENABLE_DEBUG */ +#endif /* KRB4 */ +#ifdef KRB5 + { "krb5", '5', arg_flag, &use_v5, "Use Kerberos V5", + NULL }, +#endif + { "port", 'p', arg_string, &port_str, "Use this port", + "number-of-service" }, + { "user", 'l', arg_string, &user, "Run as this user", + NULL }, + { "tcp", 't', arg_flag, &tcp_flag, + "Use a TCP connection for X11" }, + { "passive", 'P', arg_flag, &passive_flag, + "Force a passive connection" }, + { "keepalive", 'k', arg_negative_flag, &keepalive_flag, + "disable keep-alives" }, + { "debug", 'd', arg_flag, &debug_flag, + "Enable debug information" }, + { "version", 0, arg_flag, &version_flag, "Print version", + NULL }, + { "help", 0, arg_flag, &help_flag, NULL, + NULL } +}; + +static void +usage(int ret) +{ + arg_printusage (args, + sizeof(args) / sizeof(args[0]), + NULL, + "host"); + exit (ret); +} + +/* + * kx - forward an x-connection over a kerberos-encrypted channel. + */ + +int +main(int argc, char **argv) +{ + int port = 0; + int optind = 0; + int ret = 1; + char *host = NULL; + + setprogname (argv[0]); + + if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, + &optind)) + usage (1); + + if (help_flag) + usage (0); + + if (version_flag) { + print_version (NULL); + return 0; + } + + if (optind != argc - 1) + usage (1); + + host = argv[optind]; + + if (port_str) { + struct servent *s = roken_getservbyname (port_str, "tcp"); + + if (s) + port = s->s_port; + else { + char *ptr; + + port = strtol (port_str, &ptr, 10); + if (port == 0 && ptr == port_str) + errx (1, "Bad port `%s'", port_str); + port = htons(port); + } + } + + if (user == NULL) { + user = get_default_username (); + if (user == NULL) + errx (1, "who are you?"); + } + + if (!passive_flag) + passive_flag = check_for_passive (getenv("DISPLAY")); + +#if defined(HAVE_KERNEL_ENABLE_DEBUG) + if (krb_debug_flag) + krb_enable_debug (); +#endif + +#if defined(KRB4) && defined(KRB5) + if(use_v4 == -1 && use_v5 == 1) + use_v4 = 0; + if(use_v5 == -1 && use_v4 == 1) + use_v5 = 0; +#endif + +#ifdef KRB5 + if (ret && use_v5) { + if (port == 0) + port = krb5_getportbyname(NULL, "kx", "tcp", KX_PORT); + ret = doit_v5 (host, port, user, + passive_flag, debug_flag, keepalive_flag, tcp_flag); + } +#endif +#ifdef KRB4 + if (ret && use_v4) { + if (port == 0) + port = k_getportbyname("kx", "tcp", htons(KX_PORT)); + ret = doit_v4 (host, port, user, + passive_flag, debug_flag, keepalive_flag, tcp_flag); + } +#endif + return ret; +} |