This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / bsd / krshd.c
index 69688cd3bf8547668af3cce14ef79d0e4c1001a8..d625d8bd97707daa437bb8e9acb616e89670fb47 100644 (file)
@@ -25,9 +25,7 @@ char copyright[] =
  All rights reserved.\n";
 #endif /* not lint */
 
-#ifndef lint
-static char sccsid[] = "@(#)rshd.c     5.12 (Berkeley) 9/12/88";
-#endif /* not lint */
+/* based on @(#)rshd.c 5.12 (Berkeley) 9/12/88 */
 
      /*
       * remote shell server:
@@ -43,30 +41,21 @@ static char sccsid[] = "@(#)rshd.c  5.12 (Berkeley) 9/12/88";
  * 1) Check authentication.
  * 2) Check authorization via the access-control files: 
  *    ~/.k5login (using krb5_kuserok) and/or
- *    ~/.rhosts  (using ruserok).
  * Execute command if configured authoriztion checks pass, else deny 
  * permission.
  *
  * The configuration is done either by command-line arguments passed by inetd, 
  * or by the name of the daemon. If command-line arguments are present, they 
  * take priority. The options are:
- * -k and -K means check .k5login (using krb5_kuserok).
- * -r and -R means check .rhosts  (using ruserok).
- * The difference between upper and lower case is as follows:
- *    If lower case -r or -k, then as long as one of krb5_kuserok or ruserok 
- * passes, allow access. If both fail, no access. The program does not fall
- * back on password verification.
- *    If uppercase -R or -K, then those checks must be passed, regardless of 
- * other checks, else no access.
+ * -k means trust krb4 or krb5
+ * -5 means trust krb5
+ * -4 means trust krb4 (using .klogin)
  * 
- *     If no command-line arguments are present, then the presence of the 
- * letters kKrR in the program-name before "shd" determine the 
- * behaviour of the program exactly as with the command-line arguments.
  */
      
 /* DEFINES:
  *   KERBEROS - Define this if application is to be kerberised.
- *   SERVE_V4 - Define this if v4 rlogin clients are also to be served.
+ *   KRB5_KRB4_COMPAT - Define this if v4 rlogin clients are also to be served.
  *   ALWAYS_V5_KUSEROK - Define this if you want .k5login to be
  *              checked even for v4 clients (instead of .klogin).
  *   LOG_ALL_LOGINS - Define this if you want to log all logins.
@@ -81,50 +70,48 @@ static char sccsid[] = "@(#)rshd.c  5.12 (Berkeley) 9/12/88";
  *       Note:  Root account access is always logged.
  */
      
-#define SERVE_V4
 #define SERVE_NON_KRB     
 #define LOG_REMOTE_REALM
 #define LOG_CMD
-     
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <sys/time.h>
-     
-#ifdef NEED_SYS_FCNTL_H
-#include <sys/fcntl.h>
-#endif
-#ifdef USE_UNISTD_H
+   
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
 #ifdef __SCO__
 #include <sys/unistd.h>
 #endif
 
-/** XXX -- this may be bogus **/
-#if defined(CRAY) || defined(sysvimp) || defined(aux20)
-#ifndef _TYPES_
-#define _TYPES_
-#endif
-#ifndef  F_OK
-#define F_OK 0
-#endif
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#if !defined(KERBEROS) || !defined(KRB5_KRB4_COMPAT)
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
+#include <sys/socket.h>
 #endif
-/** XXX **/     
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
 
-/* not portable: #include <sys/resource.h> */
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
      
 #include <netinet/in.h>
-     
 #include <arpa/inet.h>
      
 #include <stdio.h>
+#include <grp.h>
 #include <errno.h>
 #include <pwd.h>
 #include <ctype.h>
 #include <string.h>
+#include <libpty.h>
+#include <sys/wait.h>
      
 #ifdef HAVE_SYS_LABEL_H
 /* only SunOS 4? */
@@ -132,10 +119,18 @@ static char sccsid[] = "@(#)rshd.c        5.12 (Berkeley) 9/12/88";
 #include <sys/audit.h>
 #include <pwdadj.h>
 #endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
      
 #include <signal.h>
+#if !defined(KERBEROS) || !defined(KRB5_KRB4_COMPAT)
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
 #include <netdb.h>
-     
+#endif
+
 #ifdef CRAY
 #ifndef NO_UDB
 #include <udb.h>
@@ -154,6 +149,10 @@ static char sccsid[] = "@(#)rshd.c 5.12 (Berkeley) 9/12/88";
 #endif /* CRAY */
      
 #include <syslog.h>
+
+#ifdef POSIX_TERMIOS
+#include <termios.h>
+#endif
      
 #ifdef HAVE_SYS_FILIO_H
 /* get FIONBIO from sys/filio.h, so what if it is a compatibility feature */
@@ -161,210 +160,304 @@ static char sccsid[] = "@(#)rshd.c      5.12 (Berkeley) 9/12/88";
 #endif
 
 #ifdef KERBEROS
-#include <krb5/krb5.h>
-#include <krb5/asn1.h>
-#include <krb5/crc-32.h>
-#include <krb5/mit-des.h>
-#include <krb5/ext-proto.h>
-
+#include <krb5.h>
 #include <com_err.h>
-
 #include "loginpaths.h"
-
-/** XXX - make these portable **/
-#ifdef hpux
-/* has no killpg... */
-#define killpg(pid, sig) kill(-(pid), (sig))
+#ifdef KRB5_KRB4_COMPAT
+#include <kerberosIV/krb.h>
+Key_schedule v4_schedule;
 #endif
+#include <k5-util.h>
 
-#ifdef __svr4__
-#define setpgrp(a,b) setpgrp()
-#define getpgrp(a) getpgid(a)
-/* has no killpg... */
-#define killpg(pid, sig) kill(-(pid), (sig))
+#ifdef HAVE_PATHS_H
+#include <paths.h>
 #endif
 
-#ifdef linux
-#define setpgrp(a,b) setpgid(a,b) 
+#if defined(_PATH_NOLOGIN)
+#define NOLOGIN                _PATH_NOLOGIN
+#else
+#define NOLOGIN                "/etc/nologin"
 #endif
 
-#ifdef __SCO__
-/* sco has getgroups and setgroups but no initgroups */
-int initgroups(char* name, gid_t basegid) {
-  gid_t others[NGROUPS_MAX+1];
-  int ngrps;
+#include "defines.h"
 
-  others[0] = basegid;
-  ngrps = getgroups(NGROUPS_MAX, others+1);
-  return setgroups(ngrps+1, others);
-}
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+#ifndef MAXDNAME
+#define MAXDNAME 256 /*per the rfc*/
 #endif
-/** XXX **/
 
-#define ARGSTR "rRkKD:?"
+#define ARGSTR "ek54ciD:S:M:AP:?L:w:"
+
+
+
+
+#define MAXRETRIES 4
+
+krb5_context bsd_context;
+char *srvtab = NULL;
+krb5_keytab keytab = NULL;
+krb5_ccache ccache = NULL;
+
+void fatal(int, const char *);
+
+int require_encrypt = 0;
+int do_encrypt = 0;
+int anyport = 0;
+char *kprogdir = KPROGDIR;
+int netf;
+int maxhostlen = 0;
+int stripdomain = 1;
+int always_ip = 0;
+
+static krb5_error_code recvauth(int netfd, struct sockaddr *peersin,
+                               int *valid_checksum);
+
 #else /* !KERBEROS */
-#define ARGSTR "rRD:?"
+
+#define ARGSTR "RD:?"
      
 #endif /* KERBEROS */
+
      
-int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0;
-int failed_k5 = 0;
+#ifndef HAVE_KILLPG
+#define killpg(pid, sig) kill(-(pid), (sig))
+#endif
+
+/* There are two authentication related masks:
+   * auth_ok and auth_sent.
+* The auth_ok mask is the oring of authentication systems any one
+* of which can be used.  
+* The auth_sent mask is the oring of one or more authentication/authorization
+* systems that succeeded.  If the anding
+* of these two masks is true, then authorization is successful.
+*/
+#define AUTH_KRB4 (0x1)
+#define AUTH_KRB5 (0x2)
+int auth_ok = 0, auth_sent = 0;
+int checksum_required = 0, checksum_ignored = 0;
 char *progname;
 
 #define MAX_PROG_NAME 10
 
+/* Leave room for 4 environment variables to be passed */
+#define MAXENV 4
+#define SAVEENVPAD 0,0,0,0 /* padding for envinit slots */
+char *save_env[MAXENV];
+int num_env = 0;
+
 #ifdef CRAY
 int     secflag;
 extern
 #endif /* CRAY */
-int     errno;
 
-/*VARARGS1*/
-int    error();
+void   error (char *fmt, ...)
+#if !defined (__cplusplus) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+       __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+     ;
+
+void usage(void), getstr(int, char *, int, char *), 
+    doit(int, struct sockaddr *);
+
+#ifndef HAVE_INITGROUPS
+int initgroups(char* name, gid_t basegid) {
+  gid_t others[NGROUPS_MAX+1];
+  int ngrps;
 
+  others[0] = basegid;
+  ngrps = getgroups(NGROUPS_MAX, others+1);
+  return setgroups(ngrps+1, others);
+}
+#endif
 
 
-main(argc, argv)
+int main(argc, argv)
      int argc;
      char **argv;
 {
 #if defined(BSD) && BSD+0 >= 43
     struct linger linger;
 #endif
-    int on = 1, fromlen;
-    struct sockaddr_in from;
+    int on = 1;
+    socklen_t fromlen;
+    struct sockaddr_storage from;
     extern int opterr, optind;
     extern char *optarg;
-    char *options, ch;
-    int i;
+    int ch;
     int fd;
     int debug_port = 0;
+#ifdef KERBEROS
+    krb5_error_code status;
+#endif
 
 #ifdef CRAY
     secflag = sysconf(_SC_CRAY_SECURE_SYS);
 #endif
     
-    progname = *argv;
+    progname = strrchr (*argv, '/');
+    progname = progname ? progname + 1 : *argv;
     
 #ifndef LOG_ODELAY /* 4.2 syslog */
     openlog(progname, LOG_PID);
 #else
-#ifndef LOG_DAEMON
-#define LOG_DAEMON 0
+#ifndef LOG_AUTH
+#define LOG_AUTH 0
 #endif
-    openlog(progname, LOG_PID | LOG_ODELAY, LOG_DAEMON);       
+    openlog(progname, LOG_PID | LOG_ODELAY, LOG_AUTH);
 #endif /* 4.2 syslog */
     
-    if (argc == 1) { /* Get parameters from program name. */
-       if (strlen(progname) > MAX_PROG_NAME) {
-           usage();
-           exit(1);
-       }
-       options = (char *) malloc(MAX_PROG_NAME+1);
-       options[0] = '\0';
-       for (i = 0; (progname[i] != '\0') && (i < MAX_PROG_NAME); i++)
-         if (!strcmp(progname+i, "shd")) {
-             strcpy(options, "-");
-             strncat(options, progname, i);
-             argc = 2;
-             argv[1] = options;
-             argv[2] = NULL;
-             break;
-         }
-       if (options[0] == '\0') {
-           usage();
+#ifdef KERBEROS
+    status = krb5_init_context(&bsd_context);
+    if (status) {
+           syslog(LOG_ERR, "Error initializing krb5: %s",
+                  error_message(status));
            exit(1);
-       }
     }
+#endif
     
-    /* Analyse parameters. */
+    /* Analyze parameters. */
     opterr = 0;
-    while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+    while ((ch = getopt(argc, argv, ARGSTR)) != -1)
       switch (ch) {
-       case 'r':         
-         must_pass_one = 1; /* If just 'r', any one check must succeed */
-         break;
-       case 'R':         /* If 'R', must pass .rhosts check*/
-         must_pass_rhosts = 1;
-         if (must_pass_one)
-           must_pass_one = 0;
-         break;
 #ifdef KERBEROS
        case 'k':
-         must_pass_one = 1; /* If just 'k', any one check must succeed */
+#ifdef KRB5_KRB4_COMPAT
+       auth_ok |= (AUTH_KRB5|AUTH_KRB4);
+#else
+       auth_ok |= AUTH_KRB5;
+#endif /* KRB5_KRB4_COMPAT*/
+       break;
+       
+      case '5':
+         auth_ok |= AUTH_KRB5;
+       break;
+      case 'c':
+       checksum_required = 1;
+       break;
+      case 'i':
+       checksum_ignored = 1;
+       break;
+       
+#ifdef KRB5_KRB4_COMPAT
+      case '4':
+       auth_ok |= AUTH_KRB4;
+       break;
+#endif
+         
+        case 'e':
+       require_encrypt = 1;
+         break;
+
+       case 'S':
+         if ((status = krb5_kt_resolve(bsd_context, optarg, &keytab))) {
+             com_err(progname, status, "while resolving srvtab file %s",
+                     optarg);
+             exit(2);
+         }
+         break;
+
+       case 'M':
+         krb5_set_default_realm(bsd_context, optarg);
+         break;
+
+       case 'A':
+         anyport = 1;
+         break;
+
+       case 'P':
+         kprogdir = optarg;
          break;
-       case 'K':         /* If 'K', must pass .k5login check*/
-         must_pass_k5 = 1;
-         if (must_pass_one)
-           must_pass_one = 0;
+
+        case 'L':
+         if (num_env < MAXENV) {
+                 save_env[num_env] = strdup(optarg);
+                 if(!save_env[num_env++]) {
+                         com_err(progname, ENOMEM, "in saving environment");
+                         exit(2);
+                 }               
+         } else  {
+                 fprintf(stderr, "%s: Only %d -L arguments allowed\n",
+                         progname, MAXENV);
+                 exit(2);
+         }
          break;
 #endif
        case 'D':
          debug_port = atoi(optarg);
          break;
-       case '?':
-       default:
+      case 'w':
+         if (!strcmp(optarg, "ip"))
+             always_ip = 1;
+         else {
+             char *cp;
+             cp = strchr(optarg, ',');
+             if (cp == NULL)
+                 maxhostlen = atoi(optarg);
+             else if (*(++cp)) {
+                 if (!strcmp(cp, "striplocal"))
+                     stripdomain = 1;
+                 else if (!strcmp(cp, "nostriplocal"))
+                     stripdomain = 0;
+                 else {
+                     usage();
+                     exit(1);
+                 }
+                 *(--cp) = '\0';
+                 maxhostlen = atoi(optarg);
+             }
+         }
+         break;
+      case '?':
+      default:
          usage();
          exit(1);
          break;
       }
+
+    if (optind == 0) {
+       usage();
+       exit(1);
+    }
+    
     argc -= optind;
     argv += optind;
     
     fromlen = sizeof (from);
 
-    if (debug_port) {
-       int s;
-       struct sockaddr_in sin;
-       
-       if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
-           fprintf(stderr, "Error in socket: %s\n", strerror(errno));
-           exit(2);
-       }
-       
-       memset((char *) &sin, 0,sizeof(sin));
-       sin.sin_family = AF_INET;
-       sin.sin_port = htons(debug_port);
-       sin.sin_addr.s_addr = INADDR_ANY;
-       
-       if ((bind(s, (struct sockaddr *) &sin, sizeof(sin))) < 0) {
-           fprintf(stderr, "Error in bind: %s\n", strerror(errno));
-           exit(2);
-       }
-       
-       if ((listen(s, 5)) < 0) {
-           fprintf(stderr, "Error in listen: %s\n", strerror(errno));
-           exit(2);
-       }
-       
-       if ((fd = accept(s, &from, &fromlen)) < 0) {
-           fprintf(stderr, "Error in accept: %s\n", strerror(errno));
-           exit(2);
-       }
-       
-       close(s);
-    } else {
+    if (debug_port)
+       fd = accept_a_connection(debug_port, (struct sockaddr *)&from,
+                                &fromlen);
+    else {
        if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
            fprintf(stderr, "%s: ", progname);
            perror("getpeername");
            _exit(1);
        }
-       
+
        fd = 0;
     }
-    
+
     if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
                   sizeof (on)) < 0)
-      syslog(LOG_WARNING,
-            "setsockopt (SO_KEEPALIVE): %m");
+       syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
 #if defined(BSD) && BSD+0 >= 43
     linger.l_onoff = 1;
     linger.l_linger = 60;                      /* XXX */
     if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger,
                   sizeof (linger)) < 0)
-      syslog(LOG_WARNING , "setsockopt (SO_LINGER): %m");
+       syslog(LOG_WARNING , "setsockopt (SO_LINGER): %m");
 #endif
-    doit(dup(fd), &from);
+
+    if (checksum_required&&checksum_ignored) {
+       syslog(LOG_CRIT, "Checksums are required and ignored; these options are mutually exclusive--check the documentation.");
+       fatal(fd, "Configuration error: mutually exclusive options specified");
+    }
+
+    doit(dup(fd), (struct sockaddr *) &from);
+    return 0;
 }
 
 #ifdef CRAY
@@ -378,32 +471,34 @@ char      username[20] = "USER=";
 char   homedir[64] = "HOME=";
 char   shell[64] = "SHELL=";
 char    term[64] = "TERM=network";
-char   path[128] = "PATH=";
-char   path_rest[64] = RPATH;
+char   path_rest[] = RPATH;
 
-#ifdef KERBEROS
-char    *envinit[] =
-#ifdef CRAY
-{homedir, shell, path, username, "TZ=GMT0", tmpdir, term, 0};
-#define TZENV   4
-#define TMPDIRENV 5
-char    *getenv();
-extern
-#else
-{homedir, shell, path, username, term, 0};
-#endif /* CRAY */
-#else /* !KERBEROS */
-char   *envinit[] =
+char   remote_addr[64+NI_MAXHOST]; /* = "KRB5REMOTEADDR=" */
+char   remote_port[64+NI_MAXSERV]; /* = "KRB5REMOTEPORT=" */
+char   local_addr[64+NI_MAXHOST]; /* = "KRB5LOCALADDR=" */
+char   local_port[64+NI_MAXSERV]; /* = "KRB5LOCALPORT=" */
+#define ADDRPAD 0,0,0,0
+#define KRBPAD 0               /* KRB5CCNAME, optional */
+
+/* The following include extra space for TZ and MAXENV pointers... */
+#define COMMONVARS homedir, shell, 0/*path*/, username, term
 #ifdef CRAY
-{homedir, shell, path, username, "TZ=GMT0", tmpdir, term, 0};
-#define TZENV   4
-#define TMPDIRENV 5
+char    *envinit[] = 
+{COMMONVARS, "TZ=GMT0", tmpdir, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
+#define TMPDIRENV 6
 char    *getenv();
-extern
-#else
-{homedir, shell, path, username, term, 0};
-#endif /* CRAY */
+#else /* CRAY */
+#ifdef KERBEROS
+char    *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
+#else /* KERBEROS */
+char   *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, ADDRPAD, 0};
 #endif /* KERBEROS */
+#endif /* CRAY */
+
+#define TZENV 5
+#define PATHENV 2
 
 extern char    **environ;
 char ttyn[12];         /* Line string for wtmp entries */
@@ -430,8 +525,7 @@ char *kremuser;
 krb5_principal client;
 krb5_authenticator *kdata;
 
-#ifdef SERVE_V4
-#include <kerberosIV/krb.h>
+#ifdef KRB5_KRB4_COMPAT
 AUTH_DAT       *v4_kdata;
 KTEXT          v4_ticket;
 #endif
@@ -441,28 +535,56 @@ int auth_sys = 0; /* Which version of Kerberos used to authenticate */
 #define KRB5_RECVAUTH_V4       4
 #define KRB5_RECVAUTH_V5       5
 
-doit(f, fromp)
+static krb5_sigtype
+cleanup(signumber)
+     int signumber;
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction sa;
+
+    (void)sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = SIG_IGN;
+    (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+    (void)kill(-pid, SIGTERM);
+#else
+    signal(SIGINT, SIG_IGN);
+    signal(SIGQUIT, SIG_IGN);
+    signal(SIGTERM, SIG_IGN);
+    signal(SIGPIPE, SIG_IGN);
+    signal(SIGHUP, SIG_IGN);
+    
+    killpg(pid, SIGTERM);
+#endif
+    wait(0);
+    
+    pty_logwtmp(ttyn,"","");
+    syslog(LOG_INFO ,"Daemon terminated via signal %d.", signumber);
+    if (ccache)
+       krb5_cc_destroy(bsd_context, ccache);
+    exit(0);
+}
+
+
+void doit(f, fromp)
      int f;
-     struct sockaddr_in *fromp;
+     struct sockaddr *fromp;
 {
     char *cp;
-    
 #ifdef KERBEROS
-    krb5_address peeraddr;
     krb5_error_code status;
 #endif
-
-    int tmpint;
-    
-    int ioctlval, cnt;
-    char *salt, *ttynm, *tty;
-    register char *p;
+    int valid_checksum;
+    int cnt;
     char *crypt();
-    
-#ifndef CRAY
-    struct passwd *pwd;
-#else
     struct passwd *pwd;
+    char *path;
+#ifdef CRAY
 #ifndef NO_UDB
     struct udb    *ue;
     struct udb ue_static;
@@ -481,24 +603,26 @@ doit(f, fromp)
     long packet_compart;            /* Packet compartments */
 #endif  /* CRAY */
     
-    int s;
-    struct hostent *hp;
-    char *hostname;
+    int s = -1;
+    char hostname[NI_MAXHOST];
+    char *sane_host;
+    char hostaddra[NI_MAXHOST];
+    int aierr;
     short port;
-    int pv[2], cc;
-    long ready, readfrom;
-    char buf[BUFSIZ], sig;
-    int one = 1;
-    krb5_sigtype     cleanup();
-    int fd;
-    struct sockaddr_in fromaddr;
-    int non_privileged = 0;
+    int pv[2], pw[2], px[2], cc;
+    fd_set ready, readfrom;
+    char buf[RCMD_BUFSIZ], sig;
+    struct sockaddr_storage localaddr;
+#ifdef POSIX_SIGNALS
+    struct sigaction sa;
+#endif
 
 #ifdef IP_TOS
 /* solaris has IP_TOS, but only IPTOS_* values */
 #ifdef HAVE_GETTOSBYNAME
     struct tosent *tp;
 
+
     if ((tp = gettosbyname("interactive", "tcp")) &&
        (setsockopt(f, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
 #ifdef  TOS_WARN
@@ -509,11 +633,26 @@ doit(f, fromp)
 #endif
 #endif /* IP_TOS */
     
-    fromaddr = *fromp;
+    { 
+       socklen_t sin_len = sizeof (localaddr);
+       if (getsockname(f, (struct sockaddr*)&localaddr, &sin_len) < 0) {
+           perror("getsockname");
+           exit(1);
+       }
+    }
 
+#ifdef POSIX_SIGNALS
+    (void)sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = SIG_DFL;
+    (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+    (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+#else
     signal(SIGINT, SIG_DFL);
     signal(SIGQUIT, SIG_DFL);
     signal(SIGTERM, SIG_DFL);
+#endif
 #ifdef DEBUG
     { int t = open("/dev/tty", 2);
       if (t >= 0) {
@@ -522,22 +661,25 @@ doit(f, fromp)
       }
   }
 #endif
-    fromp->sin_port = ntohs((u_short)fromp->sin_port);
-    if (fromp->sin_family != AF_INET) {
+    if (fromp->sa_family != AF_INET
+#if defined(KRB5_USE_INET6) && defined(KERBEROS)
+       && fromp->sa_family != AF_INET6
+#endif
+       ) {
        syslog(LOG_ERR , "malformed from address\n");
        exit(1);
     }
 #ifdef KERBEROS
-    krb5_init_ets();
-    if ((must_pass_rhosts || must_pass_one)
-       && (fromp->sin_port >= IPPORT_RESERVED ||
-           fromp->sin_port < IPPORT_RESERVED/2))
-      non_privileged = 1;
+    netf = f;
 #else
-    if (fromp->sin_port >= IPPORT_RESERVED ||
-           fromp->sin_port < IPPORT_RESERVED/2) {
-       syslog(LOG_ERR , "connection from bad port\n");
-       exit(1);
+    {
+       struct sockaddr_in *frompin = sa2sin(fromp);
+       frompin->sin_port = ntohs((u_short)frompin->sin_port);
+       if (frompin->sin_port >= IPPORT_RESERVED ||
+           frompin->sin_port < IPPORT_RESERVED/2) {
+           syslog(LOG_ERR , "connection from bad port\n");
+           exit(1);
+       }
     }
 #endif /* KERBEROS */
     
@@ -591,25 +733,39 @@ doit(f, fromp)
     }
     (void) alarm(0);
     if (port != 0) {
-       int lport = IPPORT_RESERVED - 1;
-       s = rresvport(&lport);
+       if (anyport) {
+           int addrfamily = fromp->sa_family;
+           s = getport(0, &addrfamily);
+       } else {
+           int lport = IPPORT_RESERVED - 1;
+#ifdef HAVE_RRESVPORT_AF
+           s = rresvport_af(&lport, fromp->sa_family);
+#else
+           s = rresvport(&lport);
+#endif
+       }
        if (s < 0) {
            syslog(LOG_ERR ,
                   "can't get stderr port: %m");
            exit(1);
        }
-#ifdef KERBEROS
-       if ((must_pass_rhosts || must_pass_one)
-           && port >= IPPORT_RESERVED)
-         non_privileged = 1;
-#else
+#ifndef KERBEROS
        if (port >= IPPORT_RESERVED) {
            syslog(LOG_ERR , "2nd port not reserved\n");
            exit(1);
        }
 #endif /* KERBEROS */
-       fromp->sin_port = htons((u_short)port);
-       if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
+       switch (fromp->sa_family) {
+       case AF_INET:
+           sa2sin(fromp)->sin_port = htons((u_short)port);
+           break;
+#ifdef KRB5_USE_INET6
+       case AF_INET6:
+           sa2sin6(fromp)->sin6_port = htons((u_short)port);
+           break;
+#endif
+       }
+       if (connect(s, (struct sockaddr *)fromp, socklen(fromp)) < 0) {
            syslog(LOG_INFO ,
                   "connect second port: %m");
            exit(1);
@@ -618,23 +774,28 @@ doit(f, fromp)
     dup2(f, 0);
     dup2(f, 1);
     dup2(f, 2);
-    hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
-                      fromp->sin_family);
-    if (hp){
-       hostname = malloc(strlen(hp->h_name) + 1);
-       strcpy(hostname,hp->h_name);
+    aierr = getnameinfo(fromp, socklen(fromp), hostname, sizeof(hostname),
+                       0, 0, 0);
+    if (aierr) {
+       error("failed to get remote host address: %s", gai_strerror(aierr));
+       exit(1);
     }
-    else {
-       hostname = malloc(strlen((char *)inet_ntoa(fromp->sin_addr)) + 1);
-       strcpy(hostname,(char *)inet_ntoa(fromp->sin_addr));
+    aierr = getnameinfo(fromp, socklen(fromp), hostaddra, sizeof(hostaddra),
+                       0, 0, NI_NUMERICHOST);
+    if (aierr) {
+       error("failed to get remote host address: %s", gai_strerror(aierr));
+       exit(1);
     }
-    peeraddr.addrtype = fromp->sin_family;
-    peeraddr.length = SIZEOF_INADDR;
-    peeraddr.contents = (krb5_octet *)&fromp->sin_addr;
-
 
 #ifdef KERBEROS
-    if (status = recvauth(f, fromaddr, peeraddr)) {
+    status = pty_make_sane_hostname((struct sockaddr *) fromp, maxhostlen,
+                                   stripdomain, always_ip, &sane_host);
+    if (status) {
+       error("failed make_sane_hostname: %s\n", error_message(status));
+       exit(1);
+    }
+
+    if ((status = recvauth(f, fromp, &valid_checksum))) {
        error("Authentication failed: %s\n", error_message(status));
        exit(1);
     }
@@ -642,6 +803,7 @@ doit(f, fromp)
     getstr(f, remuser, sizeof(remuser), "remuser");
     getstr(f, locuser, sizeof(locuser), "locuser");
     getstr(f, cmdbuf, sizeof(cmdbuf), "command");
+    rcmd_stream_init_normal();
 #endif /* KERBEROS */
     
 #ifdef CRAY
@@ -663,8 +825,9 @@ doit(f, fromp)
     pwd = getpwnam(locuser);
     if (pwd == (struct passwd *) 0 ) {
        syslog(LOG_ERR ,
-              "Principal %s (%s@%s) for local user %s has no account.\n",
-              kremuser, remuser, hostname, locuser);
+              "Principal %s (%s@%s (%s)) for local user %s has no account.\n",
+              kremuser, remuser, hostaddra, hostname,
+              locuser); /* xxx sprintf buffer in syslog*/
        error("Login incorrect.\n");
        exit(1);
     }
@@ -715,18 +878,18 @@ doit(f, fromp)
     endudb();
     if (secflag) {
        if(getsysv(&sysv, sizeof(struct sysv)) != 0) {
-           loglogin(hostname, SLG_LLERR, 0, ue);
+           loglogin(sane_host, SLG_LLERR, 0, ue);
            error("Permission denied.\n");
            exit(1);
        }
        if ((packet_level != ue->ue_deflvl) ||
            ((packet_compart & ue->ue_comparts) != packet_compart )){
-           loglogin(hostname, SLG_LLERR, 0, ue);
+           loglogin(sane_host, SLG_LLERR, 0, ue);
            error("Permission denied.\n");
            exit(1);
        }
        if (ue->ue_disabled != 0) {
-           loglogin(hostname,SLG_LOCK,ue->ue_logfails,ue);
+           loglogin(sane_host,SLG_LOCK,ue->ue_logfails,ue);
            error("Permission denied.\n");
            exit(1);
        }
@@ -759,23 +922,14 @@ doit(f, fromp)
        privileges. */
     if (port) {
        /* Place entry into wtmp */
-       sprintf(ttyn,"krsh%1d",getpid());
-#ifdef SYSV
-       logwtmp(ttyn,locuser,hostname,1,1); /*Leave wtmp open*/
-#else
-       logwtmp(ttyn,locuser,hostname,1);  /*Leave wtmp open*/
-#endif
+       sprintf(ttyn,"krsh%ld",(long) (getpid() % 9999999));
+       pty_logwtmp(ttyn,locuser,sane_host);
     }
     /*      We are simply execing a program over rshd : log entry into wtmp,
            as kexe(pid), then finish out the session right after that.
            Syslog should have the information as to what was exec'd */
     else {
-       sprintf(ttyn,"kexe%1d",getpid());
-#ifdef SYSV
-       logwtmp(ttyn,locuser,hostname,1,1);  /* Leave open wtmp */
-#else
-       logwtmp(ttyn,locuser,hostname,1);       /* Leave open wtmp */
-#endif
+       pty_logwtmp(ttyn,locuser,sane_host);
     }
     
 #ifdef CRAY
@@ -788,7 +942,7 @@ doit(f, fromp)
        if (getusrv(&usrv)){
            syslog(LOG_ERR,"Cannot getusrv");
            error("Permission denied.\n");
-           loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+           loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
            goto signout_please;
        }
        /*
@@ -797,12 +951,12 @@ doit(f, fromp)
        if((ue->ue_valcat & TFM_TRUSTED) ||
           (sysv.sy_oldtfm &&
            ((ue->ue_comparts & TRUSTED_SUBJECT) == TRUSTED_SUBJECT))) {
-           loglogin(hostname, SLG_TRSUB, ue->ue_logfails,ue);
+           loglogin(sane_host, SLG_TRSUB, ue->ue_logfails,ue);
            error("Permission denied.\n");
            goto signout_please;
        }
        
-       loglogin(hostname, SLG_OKLOG, ue->ue_logfails,ue);
+       loglogin(sane_host, SLG_OKLOG, ue->ue_logfails,ue);
        
        /*      Setup usrv structure with user udb info and 
                packet_level and packet_compart. */
@@ -872,23 +1026,22 @@ doit(f, fromp)
            if (ue->ue_minlvl > 0)
              nal_error++;
            /*
-             /*
-              *      Address not in NAL, if EXEMPT_NAL is not
-              *      true, then even an unclassified user is
-              *      not allowed.
-              */
-             if (!EXEMPT_NAL)
+            *      Address not in NAL, if EXEMPT_NAL is not
+            *      true, then even an unclassified user is
+            *      not allowed.
+            */
+           if (!EXEMPT_NAL)
                nal_error++;
-             else {
-                 usrv.sv_minlvl = 0;
-                 usrv.sv_maxlvl = 0;
-                 usrv.sv_valcmp = 0;
-                 usrv.sv_actcmp = 0;
-                 usrv.sv_actlvl = 0;
-             }
+           else {
+               usrv.sv_minlvl = 0;
+               usrv.sv_maxlvl = 0;
+               usrv.sv_valcmp = 0;
+               usrv.sv_actcmp = 0;
+               usrv.sv_actlvl = 0;
+           }
        }
        if (nal_error) {
-           loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+           loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
            error("Permission denied.\n");
            goto signout_please;
        }
@@ -898,7 +1051,7 @@ doit(f, fromp)
        sethost(paddr);
        
        if (setusrv(&usrv) == -1) {
-           loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+           loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
            error("Permission denied.\n");
            goto signout_please;
        }
@@ -911,81 +1064,72 @@ doit(f, fromp)
 #endif /*CRAY*/
     
     if (chdir(pwd->pw_dir) < 0) {
-       syslog(LOG_ERR ,
-              "Principal %s  (%s@%s) for local user %s has no home directory.\n",
-              kremuser, remuser, hostname, locuser);
-       error("No remote directory.\n");
+      if(chdir("/") < 0) {
+       error("No remote directory.\n");
        goto signout_please;
+      }
+          pwd->pw_dir = "/";
     }
 
 #ifdef KERBEROS
-    if (must_pass_k5 || must_pass_one) {
-#if (defined(ALWAYS_V5_KUSEROK) || !defined(KRB5_KRB4_COMPAT))
-       if (!krb5_kuserok(client,locuser)) {
-           syslog(LOG_ERR ,
-                  "Principal %s (%s@%s) for local user %s failed krb5_kuserok.\n",
-                  kremuser, remuser, hostname, locuser);
-           if (must_pass_k5) {
-               error("Permission denied.\n");
-               goto signout_please;
-           }
-           failed_k5 = 1;
-       }
-#else
+
+#if defined(KRB5_KRB4_COMPAT) && !defined(ALWAYS_V5_KUSEROK)
        if (auth_sys == KRB5_RECVAUTH_V4) {
            /* kuserok returns 0 if OK */
            if (kuserok(v4_kdata, locuser)){
                syslog(LOG_ERR ,
-                      "Principal %s (%s@%s) for local user %s failed kuserok.\n",
-                      kremuser, remuser, hostname, locuser);
-               if (must_pass_k5) {
-                   error("Permission denied.\n");
-                   goto signout_please;
+                      "Principal %s (%s@%s (%s)) for local user %s failed kuserok.\n",
+                      kremuser, remuser, hostaddra, hostname, locuser);
                }
-               failed_k5 = 1;
-           }
-       }
-       else {
+           else auth_sent |= AUTH_KRB4;
+       } else
+#endif
+       {
            /* krb5_kuserok returns 1 if OK */
-           if (!krb5_kuserok(client, locuser)){
+           if (!krb5_kuserok(bsd_context, client, locuser)){
                syslog(LOG_ERR ,
-                      "Principal %s (%s@%s) for local user %s failed krb5_kuserok.\n",
-                      kremuser, remuser, hostname, locuser);
-               if (must_pass_k5) {
-                   error("Permission denied.\n");
-                   goto signout_please;
-               }
-               failed_k5 = 1;
+                      "Principal %s (%s@%s (%s)) for local user %s failed krb5_kuserok.\n",
+                      kremuser, remuser, hostaddra, hostname, locuser);
            }
-       }
-#endif
-    }
-       
-    if (must_pass_rhosts || (failed_k5 && must_pass_one)) {
-       /* Cannot check .rhosts unless connection from privileged port */
-       if (non_privileged) {
-           syslog(LOG_ERR , "connection from bad port\n");
-           exit(1);
+           else
+               auth_sent |=
+                   ((auth_sys == KRB5_RECVAUTH_V4) ? AUTH_KRB4 : AUTH_KRB5);
        }
 
-       if (ruserok(hostname, pwd->pw_uid == 0,
-                   remuser, locuser) < 0) {
-           syslog(LOG_ERR ,
-                  "Principal %s (%s@%s) for local user %s failed ruserok.\n",
-                  kremuser, remuser, hostname, locuser);
-           error("Permission denied.\n");
-           goto signout_please;
-       }
-    }
+       
 #else
     if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
-       ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
+       ruserok(hostname[0] ? hostname : hostaddra,
+               pwd->pw_uid == 0, remuser, locuser) < 0) {
        error("Permission denied.\n");
        goto signout_please;
     }
 #endif /* KERBEROS */
+
+
+    if (checksum_required && !valid_checksum) {
+       if (auth_sent & AUTH_KRB5) {
+           syslog(LOG_WARNING, "Client did not supply required checksum--connection rejected.");
+           error( "You are using an old Kerberos5 client without checksum support; only newer clients are authorized.\n");
+           goto signout_please;
+       } else {
+           syslog(LOG_WARNING,
+   "Configuration error: Requiring checksums with -c is inconsistent with allowing Kerberos V4 connections.");
+       }
+    }
+    if (require_encrypt&&(!do_encrypt)) {
+       error("You must use encryption.\n");
+       goto signout_please;
+    }
+    if (!(auth_ok&auth_sent)) {
+       if (auth_sent)
+           error("Another authentication mechanism must be used to access this host.\n");
+       else
+           error("Permission denied.\n");
+       goto signout_please;
+    }
     
-    if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
+    if (pwd->pw_uid && !access(NOLOGIN, F_OK)) {
        error("Logins currently disabled.\n");
        goto signout_please;
     }
@@ -994,11 +1138,11 @@ doit(f, fromp)
     pwd = (struct passwd *) getpwnam(locuser);
     if (pwd && (pwd->pw_uid == 0)) {
 #ifdef LOG_CMD
-       syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s) as ROOT", 
-              cmdbuf, kremuser, remuser, hostname);
+       syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s (%s)) as ROOT", 
+              cmdbuf, kremuser, remuser, hostaddra, hostname);
 #else
-       syslog(LOG_NOTICE ,"Access as ROOT by principal %s (%s@%s)",
-              kremuser, remuser, hostname);
+       syslog(LOG_NOTICE ,"Access as ROOT by principal %s (%s@%s (%s))",
+              kremuser, remuser, hostaddra, hostname);
 #endif
     }
 #if defined(KERBEROS) && defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
@@ -1018,90 +1162,196 @@ doit(f, fromp)
 #if defined(LOG_REMOTE_REALM) || defined(LOG_OTHER_USERS) || defined(LOG_ALL_LOGINS)
       {
 #ifdef LOG_CMD
-         syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s) as local user %s", 
-                cmdbuf, kremuser, remuser, hostname, locuser);
+         syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s (%s)) as local user %s", 
+                cmdbuf, kremuser, remuser, hostaddra, hostname, locuser);
 #else
-         syslog(LOG_NOTICE ,"Access as %s by principal %s (%s@%s)",
-                locuser, kremuser, remuser, hostname);
+         syslog(LOG_NOTICE ,"Access as %s by principal %s (%s@%s (%s))",
+                locuser, kremuser, remuser, hostaddra, hostname);
 #endif
       }
 #endif
     
-    (void) write(2, "\0", 1);
+    (void) write(2, "", 1);
     
-    if (port) {
-       if (pipe(pv) < 0) {
+    if (port||do_encrypt) {
+       if (port&&(pipe(pv) < 0)) {
            error("Can't make pipe.\n");
            goto signout_please;
        }
+       if (pipe(pw) < 0) {
+           error("Can't make pipe 2.\n");
+           goto signout_please;
+       }
+       if (pipe(px) < 0) {
+           error("Can't make pipe 3.\n");
+           goto signout_please;
+       }
        pid = fork();
        if (pid == -1)  {
            error("Fork failed.\n");
            goto signout_please;
        }
        if (pid) {
+#ifdef POSIX_SIGNALS
+           sa.sa_handler = cleanup;
+           (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+           (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+           (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+           (void)sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+           sa.sa_handler = SIG_IGN;
+           /* SIGPIPE is a crutch that we don't need if we check 
+              the exit status of write. */
+           (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
+           (void)sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+#else
            signal(SIGINT, cleanup);
            signal(SIGQUIT, cleanup);
            signal(SIGTERM, cleanup);
-           signal(SIGPIPE, cleanup);
            signal(SIGHUP, cleanup);
+           /* SIGPIPE is a crutch that we don't need if we check 
+              the exit status of write. */
+           signal(SIGPIPE, SIG_IGN);
            signal(SIGCHLD,SIG_IGN);
+#endif
            
            (void) close(0); (void) close(1); (void) close(2);
-           (void) close(f); (void) close(pv[1]);
-           readfrom = (1L<<s) | (1L<<pv[0]);
-           ioctl(pv[0], FIONBIO, (char *)&one);
-           /* should set s nbio! */
+           if(port)
+               (void) close(pv[1]);
+           (void) close(pw[1]);
+           (void) close(px[0]);
+           
+           
+           
+           FD_ZERO(&readfrom);
+           FD_SET(f, &readfrom);
+           if(port) {
+               FD_SET(s, &readfrom);
+               FD_SET(pv[0], &readfrom);
+           }
+           FD_SET(pw[0], &readfrom);
+           
+           /* read from f, write to px[1] -- child stdin */
+           /* read from s, signal child */
+           /* read from pv[0], write to s -- child stderr */
+           /* read from pw[0], write to f -- child stdout */
+
            do {
                ready = readfrom;
-               if (select(16, &ready, (fd_set *)0,
-                          (fd_set *)0, (struct timeval *)0) < 0)
-                 break;
-               if (ready & (1L<<s)) {
-                   if (read(s, &sig, 1) <= 0)
-                     readfrom &= ~(1L<<s);
-                   else {
+               if (select(8*sizeof(ready), &ready, (fd_set *)0,
+                          (fd_set *)0, (struct timeval *)0) < 0) {
+                   if (errno == EINTR) {
+                       continue;
+                   } else {
+                       break;
+               }
+               }
+
+               if (port&&FD_ISSET(pv[0], &ready)) {
+                   /* read from the child stderr, write to the net */
+                   errno = 0;
+                   cc = read(pv[0], buf, sizeof (buf));
+                   if (cc <= 0) {
+                       shutdown(s, 1+1);
+                       FD_CLR(pv[0], &readfrom);
+                   } else {
+                       (void) rcmd_stream_write(s, buf, (unsigned) cc, 1);
+                   }
+               }
+               if (FD_ISSET(pw[0], &ready)) {
+                   /* read from the child stdout, write to the net */
+                   errno = 0;
+                   cc = read(pw[0], buf, sizeof (buf));
+                   if (cc <= 0) {
+                       shutdown(f, 1+1);
+                       FD_CLR(pw[0], &readfrom);
+                   } else {
+                       (void) rcmd_stream_write(f, buf, (unsigned) cc, 0);
+                   }
+               }
+               if (port&&FD_ISSET(s, &ready)) {
+                   /* read from the alternate channel, signal the child */
+                   if (rcmd_stream_read(s, &sig, 1, 1) <= 0) {
+                       FD_CLR(s, &readfrom);
+                   } else {
+#ifdef POSIX_SIGNALS
+                       sa.sa_handler = cleanup;
+                       (void)sigaction(sig, &sa, (struct sigaction *)0);
+                       kill(-pid, sig);
+#else
                        signal(sig, cleanup);
                        killpg(pid, sig);
+#endif
                    }
                }
-               if (ready & (1L<<pv[0])) {
+               if (FD_ISSET(f, &ready)) {
+                   /* read from the net, write to child stdin */
                    errno = 0;
-                   cc = read(pv[0], buf, sizeof (buf));
+                   cc = rcmd_stream_read(f, buf, sizeof(buf), 0);
                    if (cc <= 0) {
-                       shutdown(s, 1+1);
-                       readfrom &= ~(1L<<pv[0]);
-                   } else
-                     (void) write(s, buf, cc);
+                       (void) close(px[1]);
+                       FD_CLR(f, &readfrom);
+                   } else {
+                       int wcc;
+                       wcc = write(px[1], buf, (unsigned) cc);
+                       if (wcc == -1) {
+                         /* pipe closed, don't read any more */
+                         /* might check for EPIPE */
+                         (void) close(px[1]);
+                         FD_CLR(f, &readfrom);
+                       } else if (wcc != cc) {
+                         syslog(LOG_INFO, "only wrote %d/%d to child", 
+                                wcc, cc);
+               }
                }
-           } while (readfrom);
+               }
+           } while ((port&&FD_ISSET(s, &readfrom)) ||
+                    FD_ISSET(f, &readfrom) ||
+                    (port&&FD_ISSET(pv[0], &readfrom) )||
+                    FD_ISSET(pw[0], &readfrom));
 #ifdef KERBEROS
            syslog(LOG_INFO ,
                   "Shell process completed.");
 #endif
            /* Finish session in wmtp */
-#ifdef SYSV
-           logwtmp(ttyn,"","",0,0); /* Close wtmp */
-#else
-           logwtmp(ttyn,"","",0);   /* Close wtmp */
-#endif
+           pty_logwtmp(ttyn,"","");
+           if (ccache)
+               krb5_cc_destroy(bsd_context, ccache);
            exit(0);
        }
+#if defined(HAVE_SETSID)&&(!defined(ULTRIX))
+       setsid();
+#else
+#ifdef SETPGRP_TWOARG
        setpgrp(0, getpid());
-       (void) close(s); (void) close(pv[0]);
-       dup2(pv[1], 2);
-       (void) close(pv[1]);
+#else
+       setpgrp();
+#endif /*setpgrp_twoarg*/
+#endif /*HAVE_SETSID*/
+       (void) close(s);
+       (void) close(f);
+       (void) close(pw[0]);
+       if (port)
+           (void) close(pv[0]);
+       (void) close(px[1]);
+
+       (void) dup2(px[0], 0);
+       (void) dup2(pw[1], 1);
+       if(port)
+           (void) dup2(pv[1], 2);
+       else dup2(pw[1], 2);
+
+       (void) close(px[0]);
+       (void) close(pw[1]);
+       if(port)
+           (void) close(pv[1]);
     }
     
     /*      We are simply execing a program over rshd : log entry into wtmp, 
            as kexe(pid), then finish out the session right after that.
            Syslog should have the information as to what was exec'd */
     else {
-#ifdef SYSV
-       logwtmp(ttyn,"","",0,0);                /* Close wtmp */
-#else
-       logwtmp(ttyn,"","",0);   /* Close wtmp */
-#endif
+       pty_logwtmp(ttyn,"","");
     }
     
     if (*pwd->pw_shell == '\0')
@@ -1109,58 +1359,212 @@ doit(f, fromp)
     (void) close(f);
     (void) setgid((gid_t)pwd->pw_gid);
 #ifndef sgi
-    initgroups(pwd->pw_name, pwd->pw_gid);
+    if (getuid() == 0 || getuid() != pwd->pw_uid) {
+        /* For testing purposes, we don't call initgroups if we
+           already have the right uid, and it is not root.  This is
+           because on some systems initgroups outputs an error message
+           if not called by root.  */
+        initgroups(pwd->pw_name, pwd->pw_gid);
+    }
 #endif
+#ifdef HAVE_SETLUID
+    /*
+     * If we're on a system which keeps track of login uids, then
+     * set the login uid. 
+     */
+    setluid((uid_t) pwd->pw_uid);
+#endif /* HAVE_SETLUID */
     (void) setuid((uid_t)pwd->pw_uid);
-    environ = envinit;
+    /* if TZ is set in the parent, drag it in */
+    {
+      char **findtz = environ;
+      while(*findtz) {
+       if(!strncmp(*findtz,"TZ=",3)) {
+         envinit[TZENV] = *findtz;
+         break;
+       }
+       findtz++;
+      }
+    }
     strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
     strncat(shell, pwd->pw_shell, sizeof(shell)-7);
     strncat(username, pwd->pw_name, sizeof(username)-6);
-    strcat(path, KPROGDIR);
-    strcat(path, ":");
-    strcat(path, path_rest);
+    path = (char *) malloc(strlen(kprogdir) + strlen(path_rest) + 7);
+    if (path == NULL) {
+        perror("malloc");
+       _exit(1);
+    }
+    sprintf(path, "PATH=%s:%s", kprogdir, path_rest);
+    envinit[PATHENV] = path;
+
+    /* If we have KRB5CCNAME set, then copy into the
+     * child's environment.  This can't really have
+     * a fixed position because tz may or may not be set.
+     */
+    if (getenv("KRB5CCNAME")) {
+       int i;
+       char *buf2 = (char *)malloc(strlen(getenv("KRB5CCNAME"))
+                                          +strlen("KRB5CCNAME=")+1);
+       if (buf2) {
+         sprintf(buf2, "KRB5CCNAME=%s",getenv("KRB5CCNAME"));
+
+         for (i = 0; envinit[i]; i++);
+         envinit[i] = buf2;
+       }
+    }
+
+    {
+      char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+      int i;
+      /* these four are covered by ADDRPAD */
+
+      for (i = 0; envinit[i]; i++);
+
+      aierr = getnameinfo((struct sockaddr *)&localaddr,
+                         socklen((struct sockaddr *)&localaddr),
+                         hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+                         NI_NUMERICHOST | NI_NUMERICSERV);
+      if (aierr)
+         goto skip_localaddr_env;
+      sprintf(local_addr,  "KRB5LOCALADDR=%s", hbuf);
+      envinit[i++] =local_addr;
+
+      sprintf(local_port,  "KRB5LOCALPORT=%s", sbuf);
+      envinit[i++] =local_port;
+    skip_localaddr_env:
+
+      aierr = getnameinfo(fromp, socklen(fromp),
+                         hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+                         NI_NUMERICHOST | NI_NUMERICSERV);
+      if (aierr)
+         goto skip_remoteaddr_env;
+      sprintf(remote_addr, "KRB5REMOTEADDR=%s", hbuf);
+      envinit[i++] =remote_addr;
+
+      sprintf(remote_port, "KRB5REMOTEPORT=%s", sbuf);
+      envinit[i++] =remote_port;
+
+    skip_remoteaddr_env:
+      ;
+    }
+
+    /* If we do anything else, make sure there is space in the array. */
+
+    for(cnt=0; cnt < num_env; cnt++) {
+           int i;
+           char *buf2;
+
+           if(getenv(save_env[cnt])) {
+                   buf2 = (char *)malloc(strlen(getenv(save_env[cnt]))
+                                        +strlen(save_env[cnt]+2));
+                   if (buf2) {
+                           sprintf(buf2, "%s=%s", save_env[cnt], 
+                                   getenv(save_env[cnt]));
+                           for (i = 0; envinit[i]; i++);
+                           envinit[i] = buf2;
+                   }
+           }
+    }
+
+    /* XXX - If we do anything else, make sure there is space in the array. */
+
+    environ = envinit;
+    
+#ifdef KERBEROS
+    /* To make Kerberos rcp work correctly, we must ensure that we
+       invoke Kerberos rcp on this end, not normal rcp, even if the
+       shell startup files change PATH.  */
+    if (!strncmp(cmdbuf, "rcp ", 4) ||
+       (do_encrypt && !strncmp(cmdbuf, "-x rcp ", 7))) {
+        char *copy;
+       struct stat s2;
+       int offst = 0;
+
+       copy = malloc(strlen(cmdbuf) + 1);
+       if (copy == NULL) {
+           perror("malloc");
+           _exit(1);
+       }
+       strcpy(copy, cmdbuf);
+       if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
+               offst = 3;
+       }
+
+        strcpy((char *) cmdbuf + offst, kprogdir);
+       cp = copy + 3 + offst;
+
+       cmdbuf[sizeof(cmdbuf) - 1] = '\0';
+       if (auth_sys == KRB5_RECVAUTH_V4) {
+         strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+       } else {
+         strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+       }
+       if (stat((char *)cmdbuf + offst, &s2) >= 0)
+         strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+       else
+         strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+       free(copy);
+    }
+#endif
+
     cp = strrchr(pwd->pw_shell, '/');
     if (cp)
       cp++;
     else
       cp = pwd->pw_shell;
     
-    execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+    if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
+       execl(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 0);
+    }
+    else {
+       execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+    }
     perror(pwd->pw_shell);
     perror(cp);
     exit(1);
     
   signout_please:
-#ifdef SYSV
-    logwtmp(ttyn,"","",0,0);                /* Close wtmp */
-#else
-    logwtmp(ttyn,"","",0);   /* Close wtmp */
-#endif
+    if (ccache)
+       krb5_cc_destroy(bsd_context, ccache);
+    ccache = NULL;
+    pty_logwtmp(ttyn,"","");
     exit(1);
 }
-    
 
 
+void
+#ifdef HAVE_STDARG_H
+error(char *fmt, ...)
+#else
 /*VARARGS1*/
-error(fmt, a1, a2, a3)
+error(fmt, va_alist)
      char *fmt;
-     char *a1, *a2, *a3;
+     va_dcl
+#endif
 {
-    char buf[BUFSIZ];
+    va_list ap;
+    char buf[RCMD_BUFSIZ],  *cp = buf;
     
-    buf[0] = 1;
-    (void) sprintf(buf+1, "%s: ", progname);
-    (void) sprintf(buf+strlen(buf), fmt, a1, a2, a3);
+#ifdef HAVE_STDARG_H
+    va_start(ap, fmt);
+#else
+    va_start(ap);
+#endif
+
+    *cp++ = 1;
+    (void) sprintf(cp, "%s: ", progname);
+    (void) vsprintf(buf+strlen(buf), fmt, ap);
+    va_end(ap);
     (void) write(2, buf, strlen(buf));
     syslog(LOG_ERR ,"%s",buf+1);
 }
 
 
-
-getstr(fd, buf, cnt, err)
-     char *buf;
-     int cnt;
-     char *err;
+void getstr(fd, buf, cnt, err)
+    int fd;
+    char *buf;
+    int cnt;
+    char *err;
 {
     char c;
     
@@ -1175,37 +1579,10 @@ getstr(fd, buf, cnt, err)
     } while (c != 0);
 }
 
-
-
-krb5_sigtype 
-  cleanup()
-{
-    signal(SIGINT, SIG_IGN);
-    signal(SIGQUIT, SIG_IGN);
-    signal(SIGTERM, SIG_IGN);
-    signal(SIGPIPE, SIG_IGN);
-    signal(SIGHUP, SIG_IGN);
-    
-    killpg(pid, SIGTERM);
-    wait(0);
-    
-#ifdef SYSV
-    logwtmp(ttyn,"","",0,0); /* Close wtmp */
-#else
-    logwtmp(ttyn,"","",0);   /* Close wtmp */
-#endif
-    syslog(LOG_INFO ,"Shell process completed.");
-    exit(0);
-}
-
-
-
 #ifdef CRAY
 char *makejtmp(uid, gid, jid)
      register int uid, gid, jid;
 {
-    extern int errno;
-    
     register char *endc, *tdp = &tmpdir[strlen(tmpdir)];
     register int i;
     
@@ -1361,57 +1738,20 @@ loglogin(host, flag, failures, ue)
     return;
 }
 
-#endif CRAY
+#endif /* CRAY */
        
 
 
-usage()
+void usage()
 {
 #ifdef KERBEROS
-    syslog(LOG_ERR, "usage: kshd [-rRkK] or [r/R][k/K]shd");
+    syslog(LOG_ERR, "usage: kshd [-54ecikK]  ");
 #else
     syslog(LOG_ERR, "usage: rshd");
 #endif
 }
 
 
-
-int princ_maps_to_lname(principal, luser)      
-     krb5_principal principal;
-     char *luser;
-{
-    char kuser[10];
-    if (!(krb5_aname_to_localname(principal,
-                                 sizeof(kuser), kuser))
-       && (strcmp(kuser, luser) == 0)) {
-       return 1;
-    }
-    return 0;
-}
-
-
-int default_realm(principal)
-     krb5_principal principal;
-{
-    char *def_realm;
-    int realm_length;
-    int retval;
-    
-    realm_length = krb5_princ_realm(principal)->length;
-    
-    if (retval = krb5_get_default_realm(&def_realm)) {
-       return 0;
-    }
-    
-    if ((realm_length != strlen(def_realm)) ||
-       (memcmp(def_realm, krb5_princ_realm(principal)->data, realm_length))) {
-       free(def_realm);
-       return 0;
-    }  
-    free(def_realm);
-    return 1;
-}
-
 #ifdef KERBEROS
 
 #ifndef KRB_SENDAUTH_VLEN
@@ -1421,24 +1761,32 @@ int default_realm(principal)
 #define        KRB_SENDAUTH_VERS       "AUTHV0.1" /* MUST be KRB_SENDAUTH_VLEN
                                              chars */
 
-krb5_error_code
-recvauth(netf, peersin, peeraddr)
-     int netf;
-     struct sockaddr_in peersin;
-     krb5_address peeraddr;
+static krb5_error_code
+recvauth(netfd, peersin, valid_checksum)
+     int netfd;
+     struct sockaddr *peersin;
+     int *valid_checksum;
 {
+    krb5_auth_context auth_context = NULL;
     krb5_error_code status;
     struct sockaddr_in laddr;
-    char krb_vers[KRB_SENDAUTH_VLEN + 1];
-    int len;
-    krb5_principal server;
+    socklen_t len;
     krb5_data inbuf;
+#ifdef KRB5_KRB4_COMPAT
     char v4_instance[INST_SZ]; /* V4 Instance */
-    char v4_version[9];
+#endif
+    krb5_authenticator *authenticator;
     krb5_ticket        *ticket;
+    krb5_rcache                rcache;
+    struct passwd *pwd;
+    uid_t uid;
+    gid_t gid;
+    enum kcmd_proto kcmd_proto;
+    krb5_data version;
 
+    *valid_checksum = 0;
     len = sizeof(laddr);
-    if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
+    if (getsockname(netfd, (struct sockaddr *)&laddr, &len)) {
            exit(1);
     }
        
@@ -1448,96 +1796,252 @@ recvauth(netf, peersin, peeraddr)
 #define SIZEOF_INADDR sizeof(struct in_addr)
 #endif
 
-    if (status = krb5_sname_to_principal(NULL, "host", KRB5_NT_SRV_HST,
-                                        &server)) {
-           syslog(LOG_ERR, "parse server name %s: %s", "host",
-                  error_message(status));
-           exit(1);
-    }
-
+#ifdef KRB5_KRB4_COMPAT
     strcpy(v4_instance, "*");
+#endif
+
+    status = krb5_auth_con_init(bsd_context, &auth_context);
+    if (status)
+       return status;
+
+    status = krb5_auth_con_genaddrs(bsd_context, auth_context, netfd,
+                               KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
+    if (status)
+       return status;
+
+    status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
+    if (status) return status;
+
+    if (! rcache) {
+       krb5_principal server;
+
+       status = krb5_sname_to_principal(bsd_context, 0, 0,
+                                        KRB5_NT_SRV_HST, &server);
+       if (status) return status;
 
-    status = krb5_compat_recvauth(&netf,
-                                 "KCMDV0.1",
-                                 server, /* Specify daemon principal */
-                                 &peeraddr, /* We do want to match */
-                                            /* this against caddrs in */
-                                            /* the ticket */
-                                 0, /* use v5srvtab */
-                                 0, /* no keyproc */
-                                 0, /* no keyprocarg */
-                                 0, /* default rc_type */
-                                 0, /* no flags */
-
-                                 0, /*v4_opts*/
-                                 "rcmd", /* v4_service */
-                                 v4_instance, /* v4_instance */
-                                 &peersin, /* foriegn address */
-                                 &laddr, /* our local address */
-                                 "", /* use default srvtab */
-
-                                 &auth_sys, /* which authentication system */
-                                 0, /* no seq number */
-                                 &client, /* return client */
-                                 &ticket, /* return ticket */
-                                 &kdata, /* return authenticator */
-                                 
-                                 &v4_kdata, 0, v4_version);
+       status = krb5_get_server_rcache(bsd_context,
+                               krb5_princ_component(bsd_context, server, 0),
+                               &rcache);
+       krb5_free_principal(bsd_context, server);
+       if (status) return status;
 
+       status = krb5_auth_con_setrcache(bsd_context, auth_context, rcache);
+       if (status) return status;
+    }
+
+#ifdef KRB5_KRB4_COMPAT
+    status = krb5_compat_recvauth_version(bsd_context, &auth_context, &netfd,
+                                 NULL,         /* Specify daemon principal */
+                                 0,            /* no flags */
+                                 keytab, /* normally NULL to use v5srvtab */
+                                 0,            /* v4_opts */
+                                 "rcmd",       /* v4_service */
+                                 v4_instance,  /* v4_instance */
+                                 (struct sockaddr_in *)peersin, /* foreign address */
+                                 &laddr,       /* our local address */
+                                 "",           /* use default srvtab */
+
+                                 &ticket,      /* return ticket */
+                                 &auth_sys,    /* which authentication system*/
+                                 &v4_kdata, 0, &version);
+#else
+    status = krb5_recvauth_version(bsd_context, &auth_context, &netfd,
+                                  NULL,        /* daemon principal */
+                                  0,           /* no flags */
+                                  keytab,      /* normally NULL to use v5srvtab */
+                                  &ticket,    /* return ticket */
+                                  &version); /* application version string */
+    auth_sys = KRB5_RECVAUTH_V5;
+#endif
     if (status) {
        if (auth_sys == KRB5_RECVAUTH_V5) {
            /*
             * clean up before exiting
             */
-           getstr(netf, locuser, sizeof(locuser), "locuser");
-           getstr(netf, cmdbuf, sizeof(cmdbuf), "command");
-           getstr(netf, remuser, sizeof(locuser), "remuser");
+           getstr(netfd, locuser, sizeof(locuser), "locuser");
+           getstr(netfd, cmdbuf, sizeof(cmdbuf), "command");
+           getstr(netfd, remuser, sizeof(locuser), "remuser");
        }
        return status;
     }
 
-    getstr(netf, locuser, sizeof(locuser), "locuser");
-    getstr(netf, cmdbuf, sizeof(cmdbuf), "command");
+    getstr(netfd, locuser, sizeof(locuser), "locuser");
+    getstr(netfd, cmdbuf, sizeof(cmdbuf), "command");
 
+#ifdef KRB5_KRB4_COMPAT
     if (auth_sys == KRB5_RECVAUTH_V4) {
+       rcmd_stream_init_normal();
+       
        /* We do not really know the remote user's login name.
          * Assume it to be the same as the first component of the
         * principal's name. 
          */
        strcpy(remuser, v4_kdata->pname);
-       kremuser = (char *) malloc(strlen(v4_kdata->pname) + 1 +
-                                  strlen(v4_kdata->pinst) + 1 +
-                                  strlen(v4_kdata->prealm) + 1);
-       sprintf(kremuser, "%s/%s@%s", v4_kdata->pname,
-               v4_kdata->pinst, v4_kdata->prealm);
+
+       status = krb5_425_conv_principal(bsd_context, v4_kdata->pname,
+                                        v4_kdata->pinst, v4_kdata->prealm,
+                                        &client);
+       if (status) return status;
+
+       status = krb5_unparse_name(bsd_context, client, &kremuser);
        
-       if (status = krb5_parse_name(kremuser, &client))
-         return(status);
-       return 0;
+       return status;
     }
+#endif /* KRB5_KRB4_COMPAT */
 
     /* Must be V5  */
        
-    getstr(netf, remuser, sizeof(locuser), "remuser");
-
-    if (status = krb5_unparse_name(client, &kremuser))
+    kcmd_proto = KCMD_UNKNOWN_PROTOCOL;
+    if (version.length != 9)
+       fatal (netfd, "bad application version length");
+    if (!memcmp (version.data, "KCMDV0.1", 9))
+       kcmd_proto = KCMD_OLD_PROTOCOL;
+    if (!memcmp (version.data, "KCMDV0.2", 9))
+       kcmd_proto = KCMD_NEW_PROTOCOL;
+
+    getstr(netfd, remuser, sizeof(locuser), "remuser");
+
+    if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client, 
+                                   &kremuser)))
+       return status;
+    
+    if ((status = krb5_copy_principal(bsd_context, ticket->enc_part2->client, 
+                                     &client)))
        return status;
+    if ((status = krb5_auth_con_getauthenticator(bsd_context, auth_context,
+                                                &authenticator)))
+      return status;
     
-    if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) {
-       error("Error reading message: %s\n",
-             error_message(status));
+    if (authenticator->checksum && !checksum_ignored) {
+       struct sockaddr_storage adr;
+       unsigned int adr_length = sizeof(adr);
+       int e;
+       unsigned int buflen = strlen(cmdbuf)+strlen(locuser)+32;
+       char * chksumbuf = (char *) malloc(buflen);
+
+       if (chksumbuf == 0)
+           goto error_cleanup;
+       if (getsockname(netfd, (struct sockaddr *) &adr, &adr_length) != 0)
+           goto error_cleanup;
+
+       e = getnameinfo((struct sockaddr *)&adr, adr_length, 0, 0,
+                       chksumbuf, buflen, NI_NUMERICSERV);
+       if (e) {
+           free(chksumbuf);
+           fatal(netfd, "local error: can't examine port number");
+       }
+       if (strlen(chksumbuf) > 30) {
+           free(chksumbuf);
+           fatal(netfd, "wacky local port number?!");
+       }
+       strcat(chksumbuf, ":");
+       strcat(chksumbuf,cmdbuf);
+       strcat(chksumbuf,locuser);
+
+       status = krb5_verify_checksum(bsd_context,
+                                     authenticator->checksum->checksum_type,
+                                     authenticator->checksum,
+                                     chksumbuf, strlen(chksumbuf),
+                                     ticket->enc_part2->session->contents, 
+                                     ticket->enc_part2->session->length);
+
+    error_cleanup:
+       if (chksumbuf)
+           free(chksumbuf);
+       if (status) {
+           krb5_free_authenticator(bsd_context, authenticator);
+           return status;
+       }
+       *valid_checksum = 1;
+    }
+    krb5_free_authenticator(bsd_context, authenticator);
+
+
+    if (!strncmp(cmdbuf, "-x ", 3))
+       do_encrypt = 1;
+
+    {
+       krb5_keyblock *key;
+       status = krb5_auth_con_getrecvsubkey (bsd_context, auth_context,
+                                             &key);
+       if (status)
+           fatal (netfd, "Server can't get session subkey");
+       if (!key && do_encrypt && kcmd_proto == KCMD_NEW_PROTOCOL)
+           fatal (netfd, "No session subkey sent");
+       if (key && kcmd_proto == KCMD_OLD_PROTOCOL) {
+#ifdef HEIMDAL_FRIENDLY
+           key = 0;
+#else
+           fatal (netfd, "Session subkey not allowed in old kcmd protocol");
+#endif
+       }
+       if (key == 0)
+           key = ticket->enc_part2->session;
+       rcmd_stream_init_krb5 (key, do_encrypt, 0, 0, kcmd_proto);
+    }
+
+    /* Null out the "session" because kcmd.c references the session
+     * key here, and we do not want krb5_free_ticket() to destroy it. */
+    ticket->enc_part2->session = 0;
+
+    if ((status = krb5_read_message(bsd_context, (krb5_pointer)&netfd,
+                                   &inbuf))) {
+       error("Error reading message: %s\n", error_message(status));
        exit(1);
     }
 
     if (inbuf.length) { /* Forwarding being done, read creds */
-       if (status = rd_and_store_for_creds(&inbuf, ticket, locuser)) {
+       pwd = getpwnam(locuser);
+       if (!pwd) {
+           error("Login incorrect.\n");
+           exit(1);
+       }
+       uid = pwd->pw_uid;
+       gid = pwd->pw_gid;
+       if ((status = rd_and_store_for_creds(bsd_context, auth_context, &inbuf,
+                                            ticket, &ccache))) {
            error("Can't get forwarded credentials: %s\n",
                  error_message(status));
            exit(1);
        }
+       if (chown(krb5_cc_get_name(bsd_context, ccache), uid, gid) == -1) {
+           error("Can't chown forwarded credentials: %s\n",
+                 error_message(errno));
+           exit(1);
+       }
     }
-    krb5_free_ticket(ticket);
+    krb5_free_ticket(bsd_context, ticket);
     return 0;
 }
-
 #endif /* KERBEROS */
+
+
+
+void fatal(f, msg)
+     int f;
+     const char *msg;
+{
+    char buf[512];
+#ifndef POSIX_TERMIOS
+    int out = 1 ;          /* Output queue of f */
+#endif
+
+    buf[0] = '\01';             /* error indicator */
+    (void) sprintf(buf + 1, "%s: %s.\r\n",progname, msg);
+    if ((f == netf) && (pid > 0))
+      (void) rcmd_stream_write(f, buf, strlen(buf), 0);
+    else
+      (void) write(f, buf, strlen(buf));
+    syslog(LOG_ERR,"%s\n",msg);
+    if (pid > 0) {
+        signal(SIGCHLD,SIG_IGN);
+        kill(pid,SIGKILL);
+#ifdef POSIX_TERMIOS
+        (void) tcflush(1, TCOFLUSH);
+#else
+        (void) ioctl(f, TIOCFLUSH, (char *)&out);
+#endif
+        cleanup(-1);
+    }
+    exit(1);
+}