From 0dcb37b177b279e6f3ff08df746871b0fc3cbd51 Mon Sep 17 00:00:00 2001 From: Assar Westerlund Date: Sun, 30 Mar 1997 16:07:12 +0000 Subject: [PATCH] major rewrite, reverse mode works again. git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@1520 ec53bebd-3082-4978-b11e-865c3cabbd6b --- appl/kx/kx.c | 577 ++++++++++++++++++++++++++++---------------------- appl/kx/kx.h | 18 ++ appl/kx/kxd.c | 345 ++++++++++++++++++++---------- 3 files changed, 568 insertions(+), 372 deletions(-) diff --git a/appl/kx/kx.c b/appl/kx/kx.c index 3b3ed9801..9003b7024 100644 --- a/appl/kx/kx.c +++ b/appl/kx/kx.c @@ -100,13 +100,14 @@ usr2handler (int sig) static int connect_host (char *host, char *user, des_cblock *key, - des_key_schedule schedule, int passivep, int port) + des_key_schedule schedule, int port, + struct sockaddr_in *thisaddr, + struct sockaddr_in *thataddr) { CREDENTIALS cred; KTEXT_ST text; MSG_DAT msg; int status; - struct sockaddr_in thisaddr, thataddr; int addrlen; struct hostent *hostent; int s; @@ -116,39 +117,28 @@ connect_host (char *host, char *user, des_cblock *key, hostent = gethostbyname (host); if (hostent == NULL) { - fprintf (stderr, - "%s: gethostbyname '%s' failed: %s\n", prog, host, + warnx ("%s: gethostbyname '%s' failed: %s", prog, host, #ifdef HAVE_H_ERRNO - hstrerror(h_errno)); + hstrerror(h_errno) #else - "unknown error"); + "unknown error" #endif - return -1; + ); + return -1; } - memset (&thataddr, 0, sizeof(thataddr)); - thataddr.sin_family = AF_INET; - thataddr.sin_port = port; + memset (thataddr, 0, sizeof(*thataddr)); + thataddr->sin_family = AF_INET; + thataddr->sin_port = port; for(p = hostent->h_addr_list; *p; ++p) { - int one = 1; - - memcpy (&thataddr.sin_addr, *p, sizeof(thataddr.sin_addr)); + memcpy (&thataddr->sin_addr, *p, 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 (s < 0) + err (1, "%s: socket failed", prog); -#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) - setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (void *)&one, sizeof(one)); -#endif - - if (connect (s, (struct sockaddr *)&thataddr, sizeof(thataddr)) < 0) { - fprintf (stderr, "%s: connect(%s) failed: %s\n", prog, host, - strerror(errno)); + if (connect (s, (struct sockaddr *)thataddr, sizeof(*thataddr)) < 0) { + warn ("%s: connect(%s) failed", prog, host); close (s); continue; } else { @@ -158,61 +148,20 @@ connect_host (char *host, char *user, des_cblock *key, if (*p == NULL) 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; - } + addrlen = sizeof(*thisaddr); + if (getsockname (s, (struct sockaddr *)thisaddr, &addrlen) < 0 || + addrlen != sizeof(*thisaddr)) + err(1, "%s: getsockname(%s) failed", + prog, host); status = krb_sendauth (KOPT_DO_MUTUAL, s, &text, "rcmd", host, krb_realmofhost (host), getpid(), &msg, &cred, schedule, - &thisaddr, &thataddr, KX_VERSION); + thisaddr, thataddr, KX_VERSION); if (status != KSUCCESS) { - fprintf (stderr, "%s: %s: %s\n", prog, host, - krb_get_err_text(status)); - return -1; + warnx ("%s: %s: %s\n", prog, host, krb_get_err_text(status)); + return -1; } - memcpy(key, cred.session, sizeof(des_cblock)); - strncpy (name, user, sizeof(name)); - name[sizeof(name) - 1] = '\0'; - if (krb_net_write (s, name, sizeof(name)) != sizeof(name)) { - fprintf (stderr, "%s: write: %s\n", prog, strerror(errno)); - 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, display, display_size) != display_size) { - fprintf (stderr, "%s: read: %s\n", prog, strerror(errno)); - return -1; - } - - if (krb_net_read (s, xauthfile, xauthfile_size) != xauthfile_size) { - fprintf (stderr, "%s: read: %s\n", prog, - strerror(errno)); - return -1; - } - return s; } @@ -222,76 +171,23 @@ connect_host (char *host, char *user, des_cblock *key, */ static int -start_session(int xserver, int fd, des_cblock *iv, - des_key_schedule schedule) +passive_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 (replace_cookie (xserver, fd, XauFileName())) + return 1; + else + return copy_encrypted (xserver, fd, iv, schedule); +} - 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 +active_session (int xserver, int fd, des_cblock *iv, + des_key_schedule schedule) +{ + if (verify_and_remove_cookies (xserver, fd)) + return 1; + else + return copy_encrypted (xserver, fd, iv, schedule); } static void @@ -304,8 +200,7 @@ status_output (int debugp) pid = fork(); if (pid < 0) { - fprintf (stderr, "%s: fork: %s\n", prog, strerror(errno)); - exit (1); + err(1, "%s: fork", prog); } else if (pid > 0) { printf ("%u\t%s\t%s\n", (unsigned)pid, display, xauthfile); exit (0); @@ -316,158 +211,323 @@ status_output (int debugp) } /* - * + * Obtain an authenticated connection to `host' on `port'. Send a kx + * message saying we are `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 (char *host, char *user, int debugp, int port) +doit_passive (char *host, char *user, int debugp, int keepalivep, + int port) { des_key_schedule schedule; des_cblock key; int otherside; + struct sockaddr_in me, him; + u_char msg[1024], *p; + int len; + void *ret; + u_int32_t tmp; - otherside = connect_host (host, user, &key, schedule, 1, port); + otherside = connect_host (host, user, &key, schedule, port, + &me, &him); if (otherside < 0) return 1; +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (keepalivep) { + int one = 1; + + setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + + p = msg; + *p++ = INIT; + len = strlen(user); + p += krb_put_int (len, p, 4); + strncpy(p, user, len); + p += len; + *p++ = PASSIVE | (keepalivep ? KEEP_ALIVE : 0); + if (write_encrypted (otherside, msg, p - msg, schedule, + &key, &me, &him) < 0) + err (1, "%s: write to %s failed", prog, host); + len = read_encrypted (otherside, msg, sizeof(msg), &ret, + schedule, &key, &him, &me); + if (len < 0) + err (1, "%s: read from %s failed", prog, host); + p = (u_char *)ret; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %s: %.*s", prog, host, tmp, p); + } else if (*p != ACK) { + errx (1, "%s: %s: strange msg %d", prog, host, *p); + } else + p++; + p += krb_get_int (p, &tmp, 4, 0); + strncpy(display, p, tmp); + display[tmp] = '\0'; + p += tmp; + + p += krb_get_int (p, &tmp, 4, 0); + strncpy(xauthfile, p, tmp); + xauthfile[tmp] = '\0'; + p += tmp; + status_output (debugp); for (;;) { - char tmp[6]; pid_t child; int i; - i = krb_net_read (otherside, tmp, sizeof(tmp)); - if (i < 0) { - fprintf (stderr, "%s: read: %s\n", prog, strerror(errno)); - return 1; - } else if (i == 0) + len = read_encrypted (otherside, msg, sizeof(msg), &ret, + schedule, &key, &him, &me); + if (len < 0) + err (1, "%s: read from %s failed", prog, host); + else if (len == 0) return 0; + + p = (u_char *)ret; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %s: %.*s", prog, host, tmp, p); + } else if(*p != NEW_CONN) { + errx (1, "%s: %s: strange msg %d", prog, host, *p); + } else { + p++; + p += krb_get_int (p, &tmp, 4, 0); + } + ++nchild; child = fork (); if (child < 0) { - fprintf (stderr, "%s: fork: %s\n", prog, - strerror(errno)); + warn("%s: fork", prog); continue; } else if (child == 0) { struct sockaddr_in addr; int addrlen = sizeof(addr); int fd; - int one = 1; - int port; int xserver; - if (getpeername (otherside, (struct sockaddr *)&addr, - &addrlen) < 0) { - fprintf (stderr, "%s: getpeername: %s\n", prog, - strerror(errno)); - exit (1); - } + addr = him; close (otherside); - sscanf (tmp, "%d", &port); - addr.sin_port = htons(port); + addr.sin_port = htons(tmp); fd = socket (AF_INET, SOCK_STREAM, 0); - if (fd < 0) { - fprintf (stderr, "%s: socket: %s\n", prog, strerror(errno)); - exit (1); - } + if (fd < 0) + err(1, "%s: socket", prog); #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one, - sizeof(one)); -#endif - if (connect (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf (stderr, "%s: connect: %s\n", prog, strerror(errno)); - exit (1); + { + int one = 1; + + setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one, + sizeof(one)); } +#endif +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (keepalivep) { + 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, "%s: connect", prog); xserver = connect_local_xsocket (0); if (xserver < 0) return 1; - return start_session (xserver, fd, &key, schedule); + return passive_session (xserver, fd, &key, schedule); } else { } } } /* - * + * Allocate a local pseudo-xserver and wait for connections */ static int -doit_active (char *host, char *user, int debugpp, int tcpp, int port) +doit_active (char *host, char *user, + int debugpp, int keepalivep, int tcpp, int port) { - des_key_schedule schedule; - des_cblock key; - int rendez_vous1 = 0, rendez_vous2 = 0; - int tmp; + des_key_schedule schedule; + des_cblock key; + int otherside; + int rendez_vous1 = 0, rendez_vous2 = 0; + struct sockaddr_in me, him; + u_char msg[1024], *p; + int len = strlen(user); + void *ret; + u_int32_t tmp; + char *s; - tmp = get_xsockets (&rendez_vous1, - tcpp ? &rendez_vous2 : NULL); - if (tmp < 0) - return 1; - display_num = tmp; - strncpy(xauthfile, tempnam("/tmp", NULL), xauthfile_size); - if (create_and_write_cookie (xauthfile, cookie, cookie_len)) - return 1; - status_output (debugpp); - for (;;) { - fd_set fdset; - pid_t child; - int fd, thisfd; - int zero = 0; - int one = 1; + otherside = connect_host (host, user, &key, schedule, port, + &me, &him); + if (otherside < 0) + return 1; +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (keepalivep) { + int one = 1; - FD_ZERO(&fdset); - if (rendez_vous1) - FD_SET(rendez_vous1, &fdset); - if (rendez_vous2) - FD_SET(rendez_vous2, &fdset); - if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0) - continue; - if (rendez_vous1 && FD_ISSET(rendez_vous1, &fdset)) - thisfd = rendez_vous1; - else if (rendez_vous2 && FD_ISSET(rendez_vous2, &fdset)) - thisfd = rendez_vous2; - else - continue; - - fd = accept (thisfd, NULL, &zero); - if (fd < 0) - if (errno == EINTR) - continue; - else { - fprintf (stderr, "%s: accept: %s\n", prog, - strerror(errno)); - return 1; - } -#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one, sizeof(one)); + setsockopt (otherside, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } #endif - ++nchild; - child = fork (); - if (child < 0) { - fprintf (stderr, "%s: fork: %s\n", prog, - strerror(errno)); - continue; - } else if (child == 0) { - int kxd; - u_char zero = 0; + p = msg; + *p++ = INIT; + len = strlen(user); + p += krb_put_int (len, p, 4); + strncpy(p, user, len); + p += len; + *p++ = (keepalivep ? KEEP_ALIVE : 0); - if (rendez_vous1) - close (rendez_vous1); - if (rendez_vous2) - close (rendez_vous2); - kxd = connect_host (host, user, &key, schedule, 0, port); - 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, &key, schedule); - } else { - close (fd); - } - } + s = getenv("DISPLAY"); + if (s == NULL || (s = strchr(s, ':')) == NULL) + s = ":0"; + len = strlen (s); + p += krb_put_int (len, p, 4); + strncpy (p, s, len); + p += len; + + s = getenv("XAUTHORITY"); + if (s == NULL) + s = ""; + len = strlen (s); + p += krb_put_int (len, p, 4); + strncpy (p, s, len); + p += len; + + if (write_encrypted (otherside, msg, p - msg, schedule, + &key, &me, &him) < 0) + err (1, "%s: write to %s failed", prog, host); + + len = read_encrypted (otherside, msg, sizeof(msg), &ret, + schedule, &key, &him, &me); + if (len < 0) + err (1, "%s: read from %s failed", prog, host); + p = (u_char *)ret; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %s: %.*s", prog, host, tmp, p); + } else if (*p != ACK) { + errx (1, "%s: %s: strange msg %d", prog, host, *p); + } else + p++; + + tmp = get_xsockets (&rendez_vous1, + tcpp ? &rendez_vous2 : NULL); + if (tmp < 0) + return 1; + display_num = tmp; + if (tcpp) + sprintf (display, "localhost:%u", display_num); + else + sprintf (display, ":%u", display_num); + strncpy(xauthfile, tempnam("/tmp", NULL), xauthfile_size); + if (create_and_write_cookie (xauthfile, cookie, cookie_len)) + return 1; + status_output (debugpp); + for (;;) { + fd_set fdset; + pid_t child; + int fd, thisfd; + int zero = 0; + + FD_ZERO(&fdset); + if (rendez_vous1) + FD_SET(rendez_vous1, &fdset); + if (rendez_vous2) + FD_SET(rendez_vous2, &fdset); + if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0) + continue; + if (rendez_vous1 && FD_ISSET(rendez_vous1, &fdset)) + thisfd = rendez_vous1; + else if (rendez_vous2 && FD_ISSET(rendez_vous2, &fdset)) + thisfd = rendez_vous2; + else + continue; + + fd = accept (thisfd, NULL, &zero); + if (fd < 0) + if (errno == EINTR) + continue; + else + err(1, "%s: accept", prog); + + p = msg; + *p++ = NEW_CONN; + if (write_encrypted (otherside, msg, p - msg, schedule, + &key, &me, &him) < 0) + err (1, "%s: write to %s failed", prog, host); + len = read_encrypted (otherside, msg, sizeof(msg), &ret, + schedule, &key, &him, &me); + if (len < 0) + err (1, "%s: read from %s failed", prog, host); + p = (u_char *)ret; + if (*p == ERROR) { + p++; + p += krb_get_int (p, &tmp, 4, 0); + errx (1, "%s: %s: %.*s", prog, host, tmp, p); + } else if (*p != NEW_CONN) { + errx (1, "%s: %s: strange msg %d", prog, host, *p); + } else { + p++; + p += krb_get_int (p, &tmp, 4, 0); + } + + ++nchild; + child = fork (); + if (child < 0) { + warn("%s: fork", prog); + continue; + } else if (child == 0) { + int s; + u_char zero = 0; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + + if (rendez_vous1) + close (rendez_vous1); + if (rendez_vous2) + close (rendez_vous2); + + addr = him; + close (otherside); + + addr.sin_port = htons(tmp); + s = socket (AF_INET, SOCK_STREAM, 0); + if (s < 0) + err(1, "%s: socket", prog); +#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 (keepalivep) { + 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, "%s: connect", prog); + + return active_session (fd, s, &key, schedule); + } else { + close (fd); + } + } } static void @@ -481,24 +541,27 @@ usage(void) /* * kx - forward x connection over a kerberos-encrypted channel. * - * passive mode if $DISPLAY begins with : - */ + * passive mode if $DISPLAY begins with : */ int main(int argc, char **argv) { int passivep; + int keepalivep = 1; char *disp, *user = NULL; int debugp = 0, tcpp = 0; int c; int port = 0; prog = argv[0]; - while((c = getopt(argc, argv, "tdl:p:")) != EOF) { + while((c = getopt(argc, argv, "ktdl:p:")) != EOF) { switch(c) { case 'd' : debugp = 1; break; + case 'k': + keepalivep = 0; + break; case 't' : tcpp = 1; break; @@ -521,10 +584,8 @@ main(int argc, char **argv) usage (); if (user == NULL) { struct passwd *p = k_getpwuid (getuid ()); - if (p == NULL) { - fprintf (stderr, "%s: Who are you?\n", prog); - return 1; - } + if (p == NULL) + errx(1, "%s: Who are you?\n", prog); user = strdup (p->pw_name); } if (port == 0) @@ -536,7 +597,7 @@ main(int argc, char **argv) signal (SIGUSR1, usr1handler); signal (SIGUSR2, usr2handler); if (passivep) - return doit_passive (argv[0], user, debugp, port); + return doit_passive (argv[0], user, debugp, keepalivep, port); else - return doit_active (argv[0], user, debugp, tcpp, port); + return doit_active (argv[0], user, debugp, keepalivep, tcpp, port); } diff --git a/appl/kx/kx.h b/appl/kx/kx.h index 6c08af112..4be21059b 100644 --- a/appl/kx/kx.h +++ b/appl/kx/kx.h @@ -47,6 +47,7 @@ #endif #include +#include #include #include #include @@ -128,8 +129,25 @@ int create_and_write_cookie (char *xauthfile, size_t sz); int suspicious_address (int sock, struct sockaddr_in addr); +int +write_encrypted (int fd, void *buf, size_t len, des_key_schedule schedule, + des_cblock *session, struct sockaddr_in *me, + struct sockaddr_in *him); + +int +read_encrypted (int fd, void *buf, size_t len, void **ret, + des_key_schedule schedule, des_cblock *session, + struct sockaddr_in *him, struct sockaddr_in *me); + + #define KX_PORT 2111 #define KX_VERSION "KXSERV.2" #define COOKIE_TYPE "MIT-MAGIC-COOKIE-1" + +enum { INIT = 0, ACK = 1, NEW_CONN = 2, ERROR = 3 }; + +enum kx_flags { PASSIVE = 1, KEEP_ALIVE = 2 }; + +typedef enum kx_flags kx_flags; diff --git a/appl/kx/kxd.c b/appl/kx/kxd.c index 3d0c91bb6..365acb010 100644 --- a/appl/kx/kxd.c +++ b/appl/kx/kxd.c @@ -59,15 +59,28 @@ childhandler (int sig) SIGRETURN(0); } -static int -fatal (int fd, char *s) +static void +fatal (int fd, des_cblock *key, des_key_schedule schedule, + struct sockaddr_in *thisaddr, + struct sockaddr_in *thataddr, + char *format, ...) { - u_char err = 1; + u_char msg[1024]; + u_char *p; + va_list args; + int len; - write (fd, &err, sizeof(err)); - write (fd, s, strlen(s)+1); - syslog(LOG_ERR, s); - return err; + va_start(args, format); + p = msg; + *p++ = ERROR; + vsprintf (p + 4, format, args); + syslog (LOG_ERR, p + 4); + len = strlen (p + 4); + p += krb_put_int (len, p, 4); + p += len; + write_encrypted (fd, msg, p - msg, schedule, key, thisaddr, thataddr); + va_end(args); + exit (1); } static void @@ -80,116 +93,150 @@ cleanup(void) } static int -recv_conn (int sock, des_cblock *key, des_key_schedule schedule) +recv_conn (int sock, des_cblock *key, des_key_schedule schedule, + struct sockaddr_in *thisaddr, + struct sockaddr_in *thataddr) { int status; KTEXT_ST ticket; AUTH_DAT auth; char user[ANAME_SZ + 1]; char instance[INST_SZ + 1]; - struct sockaddr_in thisaddr, thataddr; int addrlen; char version[KRB_SENDAUTH_VLEN]; - u_char ok = 0; struct passwd *passwd; char remotehost[MaxHostNameLen]; + void *ret; + int len; + u_char msg[1024], *p; + u_int32_t tmp; + int flags; - addrlen = sizeof(thisaddr); - if (getsockname (sock, (struct sockaddr *)&thisaddr, &addrlen) < 0 || - addrlen != sizeof(thisaddr)) { - return 1; + addrlen = sizeof(*thisaddr); + if (getsockname (sock, (struct sockaddr *)thisaddr, &addrlen) < 0 || + addrlen != sizeof(*thisaddr)) { + syslog (LOG_ERR, "getsockname: %m"); + exit (1); } - addrlen = sizeof(thataddr); - if (getpeername (sock, (struct sockaddr *)&thataddr, &addrlen) < 0 || - addrlen != sizeof(thataddr)) { - return 1; + addrlen = sizeof(*thataddr); + if (getpeername (sock, (struct sockaddr *)thataddr, &addrlen) < 0 || + addrlen != sizeof(*thataddr)) { + syslog (LOG_ERR, "getpeername: %m"); + exit (1); } - inaddr2str (thataddr.sin_addr, remotehost, sizeof(remotehost)); + inaddr2str (thataddr->sin_addr, remotehost, sizeof(remotehost)); k_getsockinst (sock, instance); status = krb_recvauth (KOPT_DO_MUTUAL, sock, &ticket, "rcmd", instance, - &thataddr, &thisaddr, &auth, "", schedule, + thataddr, thisaddr, &auth, "", schedule, version); - if (status != KSUCCESS || - strncmp(version, KX_VERSION, KRB_SENDAUTH_VLEN) != 0) { - return 1; - } + if (status != KSUCCESS) + errx(1, "%s: krb_recvauth: %s", + prog, krb_get_err_text(status)); + if( strncmp(version, KX_VERSION, KRB_SENDAUTH_VLEN) != 0) + fatal(sock, key, schedule, thisaddr, thataddr, + "Bad version %s", version); memcpy(key, &auth.session, sizeof(des_cblock)); - if (krb_net_read (sock, user, sizeof(user)) != sizeof(user)) - return 1; + + len = read_encrypted (sock, msg, sizeof(msg), &ret, + schedule, key, thataddr, thisaddr); + if (len < 0) + return 1; + p = (u_char *)ret; + if (*p != INIT) + fatal(sock, key, schedule, thisaddr, thataddr, + "Bad message"); + p++; + p += krb_get_int (p, &tmp, 4, 0); + len = min(sizeof(user), tmp); + strncpy (user, p, len); + p += tmp; + user[len] = '\0'; + passwd = k_getpwnam (user); if (passwd == NULL) - return fatal (sock, "Cannot find uid"); + fatal (sock, key, schedule, thisaddr, thataddr, + "Cannot find uid"); if (kuserok(&auth, user) != 0) - return fatal (sock, "Permission denied"); + fatal (sock, key, schedule, thisaddr, thataddr, + "%s is not allowed to login as %s", + krb_unparse_name_long (auth.pname, + auth.pinst, + auth.prealm), + user); if (setgid (passwd->pw_gid) || initgroups(passwd->pw_name, passwd->pw_gid) || setuid(passwd->pw_uid)) { - return fatal (sock, "Cannot set uid"); + fatal (sock, key, schedule, thisaddr, thataddr, + "Cannot set uid"); } syslog (LOG_INFO, "from %s(%s): %s -> %s", remotehost, - inet_ntoa(thataddr.sin_addr), + inet_ntoa(thataddr->sin_addr), krb_unparse_name_long (auth.pname, auth.pinst, auth.prealm), user); - umask(077); - if (krb_net_write (sock, &ok, sizeof(ok)) != sizeof(ok)) - return 1; - - return 0; -} - -static int -start_session (int fd, int sock, des_cblock *key, - des_key_schedule schedule) -{ - u_char beg[12]; - int bigendianp; - unsigned n, d, npad, dpad; - char *protocol_name, *protocol_data; - 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 (sock, 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]; + flags = *p++; + if (!(flags & PASSIVE)) { + p += krb_get_int (p, &tmp, 4, 0); + len = min(tmp, display_size); + strncpy (display, p, len); + display[len] = '\0'; + p += tmp; + p += krb_get_int (p, &tmp, 4, 0); + len = min(tmp, xauthfile_size); + strncpy (xauthfile, p, len); + p += tmp; } - npad = (4 - (n % 4)) % 4; - dpad = (4 - (d % 4)) % 4; - protocol_name = malloc(n + npad); - protocol_data = malloc(d + dpad); - if (krb_net_read (fd, protocol_name, n + npad) != n + npad) - return 1; - if (krb_net_read (fd, protocol_data, d + dpad) != d + dpad) - return 1; - if (strncmp (protocol_name, COOKIE_TYPE, strlen(COOKIE_TYPE)) != 0) - return 1; - if (d != cookie_len || - memcmp (protocol_data, cookie, cookie_len) != 0) - return 1; - if (krb_net_write (sock, zeros, 6) != 6) - return 1; - return copy_encrypted (fd, sock, key, schedule); +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (flags & KEEP_ALIVE) { + int one = 1; + + setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + return flags; +} + +/* + * + */ + +static int +passive_session (int fd, int sock, des_cblock *key, + des_key_schedule schedule) +{ + if (verify_and_remove_cookies (fd, sock)) + return 1; + else + return copy_encrypted (fd, sock, key, schedule); } static int -doit_conn (int fd, int meta_sock, - des_cblock *key, des_key_schedule schedule) +active_session (int fd, int sock, des_cblock *key, + des_key_schedule schedule) +{ + fd = connect_local_xsocket(0); + + if (replace_cookie (fd, sock, xauthfile)) + return 1; + else + return copy_encrypted (fd, sock, key, schedule); +} + +static int +doit_conn (int fd, int meta_sock, int flags, + des_cblock *key, des_key_schedule schedule, + struct sockaddr_in *thisaddr, + struct sockaddr_in *thataddr) { int sock, sock2; int one = 1; - struct sockaddr_in thisaddr; + struct sockaddr_in addr; int addrlen; - char tmp[6]; + u_char msg[1024], *p; sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) { @@ -197,15 +244,27 @@ doit_conn (int fd, int meta_sock, return 1; } #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) - setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (void *)&one, sizeof(one)); + { + int one = 1; + setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (void *)&one, sizeof(one)); + } #endif - memset (&thisaddr, 0, sizeof(thisaddr)); - if (bind (sock, (struct sockaddr *)&thisaddr, sizeof(thisaddr)) < 0) { +#if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT) + if (flags & KEEP_ALIVE) { + int one = 1; + + setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&one, + sizeof(one)); + } +#endif + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + if (bind (sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { syslog (LOG_ERR, "bind: %m"); return 1; } - addrlen = sizeof(thisaddr); - if (getsockname (sock, (struct sockaddr *)&thisaddr, + addrlen = sizeof(addr); + if (getsockname (sock, (struct sockaddr *)&addr, &addrlen) < 0) { syslog (LOG_ERR, "getsockname: %m"); return 1; @@ -214,11 +273,16 @@ doit_conn (int fd, int meta_sock, syslog (LOG_ERR, "listen: %m"); return 1; } - sprintf (tmp, "%d", ntohs(thisaddr.sin_port)); - if (krb_net_write (meta_sock, tmp, sizeof(tmp)) != sizeof(tmp)) { + p = msg; + *p++ = NEW_CONN; + p += krb_put_int (ntohs(addr.sin_port), p, 4); + + if (write_encrypted (meta_sock, msg, p - msg, schedule, key, + thisaddr, thataddr) < 0) { syslog (LOG_ERR, "write: %m"); return 1; } + sock2 = accept (sock, (struct sockaddr *)&thisaddr, &addrlen); if (sock2 < 0) { syslog (LOG_ERR, "accept: %m"); @@ -226,39 +290,51 @@ doit_conn (int fd, int meta_sock, } close (sock); close (meta_sock); - return start_session (fd, sock2, key, schedule); + + if (flags & PASSIVE) + return passive_session (fd, sock2, key, schedule); + else + return active_session (fd, sock2, key, schedule); } /* * */ -static int -check_user_console (void) +static void +check_user_console (int fd, des_cblock *key, des_key_schedule schedule, + struct sockaddr_in *thisaddr, + struct sockaddr_in *thataddr) { struct stat sb; if (stat ("/dev/console", &sb) < 0) - return fatal (0, "Cannot stat /dev/console"); + fatal (fd, key, schedule, thisaddr, thataddr, + "Cannot stat /dev/console"); if (getuid() != sb.st_uid) - return fatal (0, "Permission denied"); - return 0; + fatal (fd, key, schedule, thisaddr, thataddr, + "Permission denied"); } +/* + * Receive a connection on `sock' and process it. + */ + static int doit(int sock, int tcpp) { - u_char passivep; des_key_schedule schedule; des_cblock key; int localx, tcpx; + struct sockaddr_in me, him; + int flags; + u_char msg[1024], *p; - if (recv_conn (sock, &key, schedule)) - return 1; - if (krb_net_read (sock, &passivep, sizeof(passivep)) != sizeof(passivep)) - return 1; - if (passivep) { + flags = recv_conn (sock, &key, schedule, &me, &him); + + if (flags & PASSIVE) { int tmp; + int len; tmp = get_xsockets (&localx, tcpp ? &tcpx : NULL); if (tmp < 0) @@ -268,19 +344,30 @@ doit(int sock, int tcpp) sprintf (display, "localhost:%u", display_num); else sprintf (display, ":%u", display_num); - if (krb_net_write (sock, display, display_size) != display_size) - return 1; strncpy(xauthfile, tempnam("/tmp", NULL), xauthfile_size); - if (krb_net_write (sock, xauthfile, xauthfile_size) - != xauthfile_size) - return 1; if(create_and_write_cookie (xauthfile, cookie, cookie_len)) return 1; + + p = msg; + *p++ = ACK; + len = strlen (display); + p += krb_put_int (len, p, 4); + strncpy (p, display, len); + p += len; + len = strlen (xauthfile); + p += krb_put_int (len, p, 4); + strncpy (p, xauthfile, len); + p += len; + + if(write_encrypted (sock, msg, p - msg, schedule, &key, + &me, &him) < 0) { + syslog (LOG_ERR, "write: %m"); + return 1; + } for (;;) { pid_t child; int fd; int zero = 0; - int one = 1; fd_set fds; FD_ZERO(&fds); @@ -316,10 +403,7 @@ doit(int sock, int tcpp) syslog (LOG_ERR, "accept: %m"); return 1; } -#if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT) - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&one, - sizeof(one)); -#endif + child = fork (); if (child < 0) { syslog (LOG_ERR, "fork: %m"); @@ -328,19 +412,52 @@ doit(int sock, int tcpp) close (localx); if (tcpp) close (tcpx); - return doit_conn (fd, sock, &key, schedule); + return doit_conn (fd, sock, flags, + &key, schedule, &me, &him); } else { close (fd); } } } else { - if (check_user_console ()) - return 1; + check_user_console (sock, &key, schedule, &me, &him); - localx = connect_local_xsocket (0); - if (localx < 0) - return 1; - return copy_encrypted (localx, sock, &key, schedule); + p = msg; + *p++ = ACK; + + if(write_encrypted (sock, msg, p - msg, schedule, &key, + &me, &him) < 0) { + syslog (LOG_ERR, "write: %m"); + return 1; + } + for (;;) { + pid_t child; + int fd; + int len; + void *ret; + + len = read_encrypted (sock, msg, sizeof(msg), &ret, + schedule, &key, + &him, &me); + if (len < 0) { + syslog (LOG_ERR, "read: %m"); + return 1; + } + p = (u_char *)ret; + if (*p != NEW_CONN) { + syslog (LOG_ERR, "bad_message: %d", *p); + return 1; + } + + child = fork (); + if (child < 0) { + syslog (LOG_ERR, "fork: %m"); + return 1; + } else if (child == 0) { + return doit_conn (localx, sock, flags, + &key, schedule, &me, &him); + } else { + } + } } }