This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / bsd / krshd.c
index 2b77593a44f8eff621d9bb2b59ec0fa1053d12d0..d625d8bd97707daa437bb8e9acb616e89670fb47 100644 (file)
@@ -41,25 +41,16 @@ char copyright[] =
  * 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:
@@ -82,7 +73,7 @@ char copyright[] =
 #define SERVE_NON_KRB     
 #define LOG_REMOTE_REALM
 #define LOG_CMD
-     
+   
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -96,7 +87,10 @@ char copyright[] =
 #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
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -111,10 +105,13 @@ char copyright[] =
 #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? */
@@ -122,10 +119,18 @@ char copyright[] =
 #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>
@@ -155,70 +160,109 @@ char copyright[] =
 #endif
 
 #ifdef KERBEROS
-#include "krb5.h"
-#include "com_err.h"
+#include <krb5.h>
+#include <com_err.h>
 #include "loginpaths.h"
+#ifdef KRB5_KRB4_COMPAT
+#include <kerberosIV/krb.h>
+Key_schedule v4_schedule;
+#endif
+#include <k5-util.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#if defined(_PATH_NOLOGIN)
+#define NOLOGIN                _PATH_NOLOGIN
+#else
+#define NOLOGIN                "/etc/nologin"
+#endif
 
-#define ARGSTR "rRxXeEkKD:S:M:AP:?"
+#include "defines.h"
 
-#define SECURE_MESSAGE "This rsh session is using DES encryption for all data transmissions.\r\n"
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
 
-#ifdef BUFSIZ
-#undef BUFSIZ
+#ifndef MAXDNAME
+#define MAXDNAME 256 /*per the rfc*/
 #endif
-#define BUFSIZ 5120
+
+#define ARGSTR "ek54ciD:S:M:AP:?L:w:"
+
+
+
 
 #define MAXRETRIES 4
 
-char des_inbuf[2*BUFSIZ];         /* needs to be > largest read size */
-krb5_encrypt_block eblock;        /* eblock for encrypt/decrypt */
-char des_outbuf[2*BUFSIZ];        /* needs to be > largest write size */
-krb5_data desinbuf,desoutbuf;
 krb5_context bsd_context;
 char *srvtab = NULL;
+krb5_keytab keytab = NULL;
+krb5_ccache ccache = NULL;
 
-void fatal();
-int v5_des_read();
-int v5_des_write();
-
-int (*des_read)() = v5_des_read;
-int (*des_write)() = v5_des_write;
+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 (*des_read)  read
-#define (*des_write) write
+#define ARGSTR "RD:?"
      
 #endif /* KERBEROS */
+
      
 #ifndef HAVE_KILLPG
 #define killpg(pid, sig) kill(-(pid), (sig))
 #endif
 
-int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0;
-int failed_k5 = 0;
+/* 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 *);
 
-#ifdef __SCO__
-/* sco has getgroups and setgroups but no initgroups */
+#ifndef HAVE_INITGROUPS
 int initgroups(char* name, gid_t basegid) {
   gid_t others[NGROUPS_MAX+1];
   int ngrps;
@@ -230,100 +274,89 @@ int initgroups(char* name, gid_t basegid) {
 #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;
     int ch;
-    int i;
     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 */
     
 #ifdef KERBEROS
-    krb5_init_context(&bsd_context);
-    krb5_init_ets(bsd_context);
-#endif
-    
-    if (argc == 1) { /* Get parameters from program name. */
-       if (strlen(progname) > MAX_PROG_NAME) {
-           usage();
+    status = krb5_init_context(&bsd_context);
+    if (status) {
+           syslog(LOG_ERR, "Error initializing krb5: %s",
+                  error_message(status));
            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);
-             options[i+1] = '\0';
-#if 0
-             /*
-              * Since we are just going to break out afterwards, we'll
-              * re-use the variable "i" to move the command line args.
-              */
-             for (i=argc-1; i>0; i--) argv[i+1] = argv[i];
-#endif
-             argv[++argc] = NULL;
-
-             argv[1] = options;
-             break;
-         }
     }
+#endif
     
     /* 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 */
-         break;
-       case 'K':         /* If 'K', must pass .k5login check*/
-         must_pass_k5 = 1;
-         if (must_pass_one)
-           must_pass_one = 0;
-         break;
+#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 'x':
-        case 'X':
         case 'e':
-        case 'E':
-         do_encrypt = 1;
+       require_encrypt = 1;
          break;
 
        case 'S':
-         srvtab = optarg;
+         if ((status = krb5_kt_resolve(bsd_context, optarg, &keytab))) {
+             com_err(progname, status, "while resolving srvtab file %s",
+                     optarg);
+             exit(2);
+         }
          break;
 
        case 'M':
@@ -337,12 +370,48 @@ main(argc, argv)
        case 'P':
          kprogdir = optarg;
          break;
+
+        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;
@@ -358,61 +427,37 @@ main(argc, argv)
     
     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;
-       
-       (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
-                         (char *)&on, sizeof(on));
-
-       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
@@ -428,22 +473,31 @@ char      shell[64] = "SHELL=";
 char    term[64] = "TERM=network";
 char   path_rest[] = RPATH;
 
+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
-char    *envinit[] =
-{homedir, shell, 0, 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();
 #else /* CRAY */
 #ifdef KERBEROS
-char    *envinit[] =
-{homedir, shell, 0, username, term, 0};
+char    *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
 #else /* KERBEROS */
-char   *envinit[] =
-{homedir, shell, 0, username, term, 0};
+char   *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, ADDRPAD, 0};
 #endif /* KERBEROS */
 #endif /* CRAY */
 
+#define TZENV 5
 #define PATHENV 2
 
 extern char    **environ;
@@ -471,35 +525,65 @@ char *kremuser;
 krb5_principal client;
 krb5_authenticator *kdata;
 
-#include <kerberosIV/krb.h>
+#ifdef KRB5_KRB4_COMPAT
 AUTH_DAT       *v4_kdata;
 KTEXT          v4_ticket;
+#endif
 
 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();
     struct passwd *pwd;
     char *path;
-    
 #ifdef CRAY
 #ifndef NO_UDB
     struct udb    *ue;
@@ -519,18 +603,16 @@ 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], pw[2], px[2], cc;
     fd_set ready, readfrom;
-    char buf[BUFSIZ], sig;
-    int one = 1;
-    krb5_sigtype     cleanup();
-    int fd;
-    struct sockaddr_in fromaddr;
-    int non_privileged = 0;
+    char buf[RCMD_BUFSIZ], sig;
+    struct sockaddr_storage localaddr;
 #ifdef POSIX_SIGNALS
     struct sigaction sa;
 #endif
@@ -540,6 +622,7 @@ doit(f, fromp)
 #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
@@ -550,7 +633,13 @@ 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);
@@ -572,24 +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
     netf = f;
-    desinbuf.data = des_inbuf;
-    desoutbuf.data = des_outbuf;
-    if ((must_pass_rhosts || must_pass_one)
-       && (fromp->sin_port >= IPPORT_RESERVED ||
-           fromp->sin_port < IPPORT_RESERVED/2))
-      non_privileged = 1;
 #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 */
     
@@ -643,31 +733,39 @@ doit(f, fromp)
     }
     (void) alarm(0);
     if (port != 0) {
-       int lport;
        if (anyport) {
-           lport = 5120; /* arbitrary */
-           s = getport(&lport);
+           int addrfamily = fromp->sa_family;
+           s = getport(0, &addrfamily);
        } else {
-           lport = IPPORT_RESERVED - 1;
+           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);
@@ -676,32 +774,36 @@ 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);
     }
-    if (!strncmp(cmdbuf, "-x ", 3))
-       do_encrypt = 1;
 #else
     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
@@ -723,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);
     }
@@ -775,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);
        }
@@ -819,14 +922,14 @@ doit(f, fromp)
        privileges. */
     if (port) {
        /* Place entry into wtmp */
-       sprintf(ttyn,"krsh%1d",getpid());
-       logwtmp(ttyn,locuser,hostname);
+       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 {
-       logwtmp(ttyn,locuser,hostname);
+       pty_logwtmp(ttyn,locuser,sane_host);
     }
     
 #ifdef CRAY
@@ -839,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;
        }
        /*
@@ -848,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. */
@@ -923,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;
        }
@@ -949,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;
        }
@@ -962,70 +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(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 auth_sent |= AUTH_KRB4;
        } else
 #endif
        {
            /* krb5_kuserok returns 1 if OK */
            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);
            }
-       }
-    }
-       
-    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;
     }
@@ -1034,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)
@@ -1058,19 +1162,19 @@ 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, "", 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;
        }
@@ -1093,48 +1197,83 @@ doit(f, fromp)
            (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);
 
            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(pv[1]);
+           if(port)
+               (void) close(pv[1]);
            (void) close(pw[1]);
            (void) close(px[0]);
            
-           ioctl(pv[0], FIONBIO, (char *)&one);
-           ioctl(pw[0], FIONBIO, (char *)&one);
-           /* should set s nbio! */
-
-           if (do_encrypt)
-               if (((*des_write)(s, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0)
-                   fatal(pw[0], "Cannot encrypt-write network.");
+           
            
            FD_ZERO(&readfrom);
            FD_SET(f, &readfrom);
-           FD_SET(s, &readfrom);
-           FD_SET(pv[0], &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(8*sizeof(ready), &ready, (fd_set *)0,
-                          (fd_set *)0, (struct timeval *)0) < 0)
-                 break;
-               if (FD_ISSET(s, &ready)) {
-                   if ((*des_read)(s, &sig, 1) <= 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 {
+                   else {
 #ifdef POSIX_SIGNALS
                        sa.sa_handler = cleanup;
                        (void)sigaction(sig, &sa, (struct sigaction *)0);
@@ -1146,69 +1285,73 @@ doit(f, fromp)
                    }
                }
                if (FD_ISSET(f, &ready)) {
+                   /* read from the net, write to child stdin */
                    errno = 0;
-                   cc = (*des_read)(f, buf, sizeof(buf));
+                   cc = rcmd_stream_read(f, buf, sizeof(buf), 0);
                    if (cc <= 0) {
                        (void) close(px[1]);
                        FD_CLR(f, &readfrom);
-                   } else
-                       (void) write(px[1], buf, cc);
+                   } 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);
                }
-               if (FD_ISSET(pv[0], &ready)) {
-                   errno = 0;
-                   cc = read(pv[0], buf, sizeof (buf));
-                   if (cc <= 0) {
-                       shutdown(s, 1+1);
-                       FD_CLR(pv[0], &readfrom);
-                   } else
-                       (void) (*des_write)(s, buf, cc);
                }
-               if (FD_ISSET(pw[0], &ready)) {
-                   errno = 0;
-                   cc = read(pw[0], buf, sizeof (buf));
-                   if (cc <= 0) {
-                       shutdown(f, 1+1);
-                       FD_CLR(pw[0], &readfrom);
-                   } else
-                       (void) (*des_write)(f, buf, cc);
                }
-           } while (FD_ISSET(s, &readfrom) ||
+           } while ((port&&FD_ISSET(s, &readfrom)) ||
                     FD_ISSET(f, &readfrom) ||
-                    FD_ISSET(pv[0], &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 */
-           logwtmp(ttyn,"","");
+           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());
 #else
        setpgrp();
-#endif
+#endif /*setpgrp_twoarg*/
+#endif /*HAVE_SETSID*/
        (void) close(s);
        (void) close(f);
        (void) close(pw[0]);
-       (void) close(pv[0]);
+       if (port)
+           (void) close(pv[0]);
        (void) close(px[1]);
 
        (void) dup2(px[0], 0);
        (void) dup2(pw[1], 1);
-       (void) dup2(pv[1], 2);
+       if(port)
+           (void) dup2(pv[1], 2);
+       else dup2(pw[1], 2);
 
        (void) close(px[0]);
        (void) close(pw[1]);
-       (void) close(pv[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 {
-       logwtmp(ttyn,"","");
+       pty_logwtmp(ttyn,"","");
     }
     
     if (*pwd->pw_shell == '\0')
@@ -1224,8 +1367,25 @@ doit(f, fromp)
         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);
@@ -1236,12 +1396,80 @@ doit(f, fromp)
     }
     sprintf(path, "PATH=%s:%s", kprogdir, path_rest);
     envinit[PATHENV] = path;
-    cp = strrchr(pwd->pw_shell, '/');
-    if (cp)
-      cp++;
-    else
-      cp = pwd->pw_shell;
 
+    /* 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
@@ -1249,7 +1477,8 @@ doit(f, fromp)
     if (!strncmp(cmdbuf, "rcp ", 4) ||
        (do_encrypt && !strncmp(cmdbuf, "-x rcp ", 7))) {
         char *copy;
-       struct stat s;
+       struct stat s2;
+       int offst = 0;
 
        copy = malloc(strlen(cmdbuf) + 1);
        if (copy == NULL) {
@@ -1258,58 +1487,84 @@ doit(f, fromp)
        }
        strcpy(copy, cmdbuf);
        if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
-           strcpy(cmdbuf + 3, kprogdir);
-           cp = copy + 6;
+               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 {
-           strcpy(cmdbuf, kprogdir);
-           cp = copy + 3;
+         strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
        }
-       strcat(cmdbuf, "/rcp");
-       if (stat(cmdbuf, &s) >= 0)
-         strcat(cmdbuf, cp);
+       if (stat((char *)cmdbuf + offst, &s2) >= 0)
+         strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
        else
-         strcpy(cmdbuf, copy);
+         strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
        free(copy);
     }
 #endif
 
+    cp = strrchr(pwd->pw_shell, '/');
+    if (cp)
+      cp++;
+    else
+      cp = pwd->pw_shell;
+    
     if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
        execl(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 0);
     }
-    else
+    else {
        execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
-
+    }
     perror(pwd->pw_shell);
     perror(cp);
     exit(1);
     
   signout_please:
-    logwtmp(ttyn,"","");
+    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;
     
@@ -1324,48 +1579,10 @@ getstr(fd, buf, cnt, err)
     } while (c != 0);
 }
 
-
-
-krb5_sigtype 
-  cleanup()
-{
-#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);
-    
-    logwtmp(ttyn,"","");
-    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;
     
@@ -1525,54 +1742,16 @@ loglogin(host, flag, failures, ue)
        
 
 
-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(bsd_context, 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(bsd_context, principal)->length;
-    
-    if (retval = krb5_get_default_realm(bsd_context, &def_realm)) {
-       return 0;
-    }
-    
-    if ((realm_length != strlen(def_realm)) ||
-       (memcmp(def_realm, krb5_princ_realm(bsd_context, principal)->data, 
-               realm_length))) {
-       free(def_realm);
-       return 0;
-    }  
-    free(def_realm);
-    return 1;
-}
-
 #ifdef KERBEROS
 
 #ifndef KRB_SENDAUTH_VLEN
@@ -1582,25 +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_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);
     }
        
@@ -1610,244 +1796,240 @@ recvauth(netf, peersin, peeraddr)
 #define SIZEOF_INADDR sizeof(struct in_addr)
 #endif
 
-    if (status = krb5_sname_to_principal(bsd_context, 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;
 
-    if (status = krb5_auth_con_init(bsd_context, &auth_context))
+    status = krb5_auth_con_genaddrs(bsd_context, auth_context, netfd,
+                               KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
+    if (status)
        return status;
 
-    krb5_auth_con_setaddrs(bsd_context, auth_context, NULL, &peeraddr);
+    status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
+    if (status) return status;
 
-    status = krb5_compat_recvauth(bsd_context, &auth_context, &netf,
-                                 "KCMDV0.1",
-                                 server, /* Specify daemon principal */
-                                 0,            /* default rc_type */
+    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_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 */
-                                 srvtab, /* normally NULL to use v5srvtab */
+                                 keytab, /* normally NULL to use v5srvtab */
                                  0,            /* v4_opts */
                                  "rcmd",       /* v4_service */
                                  v4_instance,  /* v4_instance */
-                                 &peersin,     /* foreign address */
+                                 (struct sockaddr_in *)peersin, /* foreign address */
                                  &laddr,       /* our local address */
                                  "",           /* use default srvtab */
 
                                  &ticket,      /* return ticket */
                                  &auth_sys,    /* which authentication system*/
-                                 &v4_kdata, 0, v4_version);
-
+                                 &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(bsd_context, 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(bsd_context, ticket->enc_part2->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;
     
-    /* Setup eblock for encrypted sessions. */
-    krb5_use_keytype(bsd_context, &eblock, ticket->enc_part2->session->keytype);
-    if (status = krb5_process_key(bsd_context, &eblock, ticket->enc_part2->session))
-       fatal(netf, "Permission denied");
+    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 eblock.key references the session
+    /* 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)&netf, &inbuf)) {
+    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(bsd_context, auth_context, &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(bsd_context, ticket);
     return 0;
 }
+#endif /* KERBEROS */
 
 
-char storage[2*BUFSIZ];                    /* storage for the decryption */
-int nstored = 0;
-char *store_ptr = storage;
-
-int
-v5_des_read(fd, buf, len)
-     int fd;
-     register char *buf;
-     int len;
-{
-    int nreturned = 0;
-    krb5_ui_4 net_len,rd_len;
-    int cc,retry;
-    unsigned char len_buf[4];
-    
-    if (!do_encrypt)
-      return(read(fd, buf, len));
-    
-    if (nstored >= len) {
-       memcpy(buf, store_ptr, len);
-       store_ptr += len;
-       nstored -= len;
-       return(len);
-    } else if (nstored) {
-       memcpy(buf, store_ptr, nstored);
-       nreturned += nstored;
-       buf += nstored;
-       len -= nstored;
-       nstored = 0;
-    }
-    
-    if ((cc = krb5_net_read(bsd_context, fd, (char *)len_buf, 4)) != 4) {
-       if ((cc < 0)  && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
-           return(cc);
-       /* XXX can't read enough, pipe must have closed */
-       return(0);
-    }
-    rd_len =
-       ((len_buf[0]<<24) |
-        (len_buf[1]<<16) |
-        (len_buf[2]<<8) |
-        len_buf[3]);
-    net_len = krb5_encrypt_size(rd_len, eblock.crypto_entry);
-    if (net_len < 0 || net_len > sizeof(des_inbuf)) {
-       /* XXX preposterous length, probably out of sync.
-          act as if pipe closed */
-       syslog(LOG_ERR,"Read size problem (rd_len=%d, net_len=%d)",
-              rd_len, net_len);
-       return(0);
-    }
-    retry = 0;
-  datard:
-    if ((cc = krb5_net_read(bsd_context, fd, desinbuf.data, net_len)) != net_len) {
-       /* XXX can't read enough, pipe must have closed */
-       if ((cc < 0)  && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
-           retry++;
-           if (retry > MAXRETRIES){
-               syslog(LOG_ERR, "des_read retry count exceeded %d\n", retry);
-               return(0);
-           }
-           sleep(1);
-           goto datard;
-       }
-       syslog(LOG_ERR,
-              "Read data received %d != expected %d.",
-              cc, net_len);
-       return(0);
-    }
-
-    /* decrypt info */
-    if (krb5_decrypt(bsd_context, desinbuf.data, (krb5_pointer) storage, net_len,
-                    &eblock, 0)) {
-       syslog(LOG_ERR,"Read decrypt problem.");
-       return(0);
-    }
-
-    store_ptr = storage;
-    nstored = rd_len;
-    if (nstored > len) {
-       memcpy(buf, store_ptr, len);
-       nreturned += len;
-       store_ptr += len;
-       nstored -= len;
-    } else {
-       memcpy(buf, store_ptr, nstored);
-       nreturned += nstored;
-       nstored = 0;
-    }
-    return(nreturned);
-}
-    
-
-int
-v5_des_write(fd, buf, len)
-     int fd;
-     char *buf;
-     int len;
-{
-    unsigned char len_buf[4];
-    
-    if (!do_encrypt)
-      return(write(fd, buf, len));
-    
-    desoutbuf.length = krb5_encrypt_size(len, eblock.crypto_entry);
-    if (desoutbuf.length > sizeof(des_outbuf)){
-       syslog(LOG_ERR,"Write size problem (%d > %d)",
-              desoutbuf.length, sizeof(des_outbuf));
-       return(-1);
-    }
-
-    if (krb5_encrypt(bsd_context, (krb5_pointer)buf, desoutbuf.data, len, &eblock, 0)) {
-       syslog(LOG_ERR,"Write encrypt problem.");
-       return(-1);
-    }
-    
-    len_buf[0] = (len & 0xff000000) >> 24;
-    len_buf[1] = (len & 0xff0000) >> 16;
-    len_buf[2] = (len & 0xff00) >> 8;
-    len_buf[3] = (len & 0xff);
-    (void) write(fd, len_buf, 4);
-
-    if (write(fd, desoutbuf.data, desoutbuf.length) != desoutbuf.length){
-       syslog(LOG_ERR,"Could not write out all data.");
-       return(-1);
-    }
-    else return(len);
-}
-
 
 void fatal(f, msg)
      int f;
-     char *msg;
+     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) (*des_write)(f, buf, strlen(buf));
+      (void) rcmd_stream_write(f, buf, strlen(buf), 0);
     else
       (void) write(f, buf, strlen(buf));
     syslog(LOG_ERR,"%s\n",msg);
@@ -1859,8 +2041,7 @@ void fatal(f, msg)
 #else
         (void) ioctl(f, TIOCFLUSH, (char *)&out);
 #endif
-        cleanup();
+        cleanup(-1);
     }
     exit(1);
 }
-#endif /* KERBEROS */