This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / bsd / krlogind.c
index 7170e9dfecb986ffc17a8269c901f93850306015..d2979e1416331f977b3958287b13d708a2df1a86 100644 (file)
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * All rights reserved.
+ * 
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
 #ifndef lint
 char copyright[] =
   "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
@@ -41,29 +67,22 @@ char copyright[] =
  * 1) Check authentication.
  * 2) Check authorization via the access-control files: 
  *    ~/.k5login (using krb5_kuserok) and/or
- *    ~/.rhosts  (using ruserok).
  * 3) Prompt for password if any checks fail, or if so configured.
  * Allow login if all goes well either by calling the accompanying 
  * login.krb5 or /bin/login, according to the definition of 
- * DO_NOT_USE_K_LOGIN.
+ * DO_NOT_USE_K_LOGIN.l
  * 
  * 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).
+ * -k means trust krb4 or krb5
+* -5 means trust krb5
+* -4 means trust krb4
  * -p and -P means prompt for password.
- * 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 login without password. If the -p option is
- * passed with -r or -k, then if both checks fail, allow login but
- * only after password verification. 
- *    If uppercase -R or -K, then those checks must be passed,
- * regardless of other checks, else no login with or without password.
  *    If the -P option is passed, then the password is verified in 
  * addition to all other checks. If -p is not passed with -k or -r,
  * and both checks fail, then login permission is denied.
- *    -x and -e means use encryption.
+ *    - -e means use encryption.
  *
  *    If no command-line arguments are present, then the presence of the 
  * letters kKrRexpP in the program-name before "logind" determine the 
@@ -77,9 +96,7 @@ char copyright[] =
  *   KERBEROS - Define this if application is to be kerberised.
  *   CRYPT    - Define this if encryption is to be an option.
  *   DO_NOT_USE_K_LOGIN - Define this if you want to use /bin/login
- *              instead  of the accompanying login.krb5. In that case,
- *              the remote user's name must be present in the local
- *              .rhosts file, regardless of any options specified.
+ *              instead  of the accompanying login.krb5. 
  *   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).
@@ -99,7 +116,7 @@ char copyright[] =
  */
 #define LOG_REMOTE_REALM
 #define CRYPT
-
+#define USE_LOGIN_F
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -114,7 +131,10 @@ char copyright[] =
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#ifndef KERBEROS
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
 #include <sys/socket.h>
+#endif
 #include <sys/ioctl.h>
 #include <sys/wait.h>
 #include <sys/file.h>
@@ -134,7 +154,7 @@ char copyright[] =
      
 #include <signal.h>
 
-#ifdef hpux
+#if defined(hpux) || defined(__hpux)
 #include <sys/ptyio.h>
 #endif
 #ifdef sysvimp
@@ -155,12 +175,14 @@ char copyright[] =
 #else
 #include <sgtty.h>
 #endif
-     
+
+#ifndef KERBEROS
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
 #include <netdb.h>
+#endif
 #include <syslog.h>
 #include <string.h>
 #include <sys/param.h>
-#include <utmp.h>
 
 #ifdef HAVE_STREAMS
 /* krlogin doesn't test sys/tty... */
@@ -187,14 +209,15 @@ char copyright[] =
 #include <sys/filio.h>
 #endif
 
-#ifndef SETPGRP_TWOARG
-#define setpgrp(a,b) setpgrp()
-#endif
-
 #ifndef HAVE_KILLPG
 #define killpg(pid, sig) kill(-(pid), (sig))
 #endif
 
+#ifdef HAVE_PTSNAME
+/* HP/UX 9.04 has but does not declare ptsname.  */
+extern char *ptsname ();
+#endif
+
 #ifdef NO_WINSIZE
 struct winsize {
     unsigned short ws_row, ws_col;
@@ -206,13 +229,18 @@ struct winsize {
 #define roundup(x,y) ((((x)+(y)-1)/(y))*(y))
 #endif
 
+#include "fake-addrinfo.h"
+
 #ifdef KERBEROS
      
-#include "krb5.h"
+#include <krb5.h>
+#ifdef KRB5_KRB4_COMPAT
 #include <kerberosIV/krb.h>
-
-#ifdef BUFSIZ
-#undef BUFSIZ
+#endif
+#include <libpty.h>
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#include <k5-util.h>
 #endif
 
 int auth_sys = 0;      /* Which version of Kerberos used to authenticate */
@@ -223,35 +251,26 @@ int auth_sys = 0; /* Which version of Kerberos used to authenticate */
 int non_privileged = 0; /* set when connection is seen to be from */
                        /* a non-privileged port */
 
+#ifdef KRB5_KRB4_COMPAT
 AUTH_DAT       *v4_kdata;
 Key_schedule v4_schedule;
-int v4_des_read(), v4_des_write();
-
-#define BUFSIZ 5120
-
-int v5_des_read(), v5_des_write();
+#endif
 
 #include "com_err.h"
+#include "defines.h"
      
-#define SECURE_MESSAGE  "This rlogin session is using DES encryption for all data transmissions.\r\n"
-
-int (*des_read)(), (*des_write)();
-char des_inbuf[2*BUFSIZ];         /* needs to be > largest read size */
-char des_outbuf[2*BUFSIZ];        /* needs to be > largest write size */
-krb5_data desinbuf,desoutbuf;
-krb5_encrypt_block eblock;        /* eblock for encrypt/decrypt */
+#define SECURE_MESSAGE  "This rlogin session is encrypting all data transmissions.\r\n"
 
 krb5_authenticator      *kdata;
 krb5_ticket     *ticket = 0;
 krb5_context bsd_context;
+krb5_ccache ccache = NULL;
 
 krb5_keytab keytab = NULL;
 
-#define ARGSTR "rRkKeExXpPD:S:M:L:?"
+#define ARGSTR "k54ciepPD:S:M:L:fw:?"
 #else /* !KERBEROS */
-#define ARGSTR "rRpPD:?"
-#define (*des_read)  read
-#define (*des_write) write
+#define ARGSTR "rpPD:f?"
 #endif /* KERBEROS */
 
 #ifndef LOGIN_PROGRAM
@@ -275,19 +294,32 @@ char *login_program = LOGIN_PROGRAM;
 #define        UT_NAMESIZE     sizeof(((struct utmp *)0)->ut_name)
 #endif
 
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+#ifndef MAXDNAME
+#define MAXDNAME 256 /*per the rfc*/
+#endif
+
 char           lusername[UT_NAMESIZE+1];
 char           rusername[UT_NAMESIZE+1];
 char            *krusername = 0;
 char           term[64];
-char            rhost_name[128];
+char            rhost_name[MAXDNAME];
+char           rhost_addra[16];
 krb5_principal  client;
+int            do_inband = 0;
 
-extern int errno;
 int    reapchild();
 char   *progname;
 
 static int Pfd;
 
+#if defined(NEED_DAEMON_PROTO)
+extern int daemon(int, int);
+#endif
+
 #if (defined(_AIX) && defined(i386)) || defined(ibm032) || (defined(vax) && !defined(ultrix)) || (defined(SunOS) && SunOS > 40) || defined(solaris20)
 #define VHANG_FIRST
 #endif
@@ -296,97 +328,97 @@ static    int Pfd;
 #define VHANG_LAST             /* vhangup must occur on close, not open */
 #endif
 
-void   fatal(), fatalperror(), doit(), usage(), do_krb_login();
-int    princ_maps_to_lname(), default_realm();
-krb5_sigtype   cleanup();
-
-int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0;
+void   fatal(int, const char *), fatalperror(int, const char *), doit(int, struct sockaddr *), usage(void), do_krb_login(char *, char *), getstr(int, char *, int, char *);
+void   protocol(int, int);
+int    princ_maps_to_lname(krb5_principal, char *), default_realm(krb5_principal);
+krb5_sigtype   cleanup(int);
+krb5_error_code recvauth(int *);
+
+/* 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 do_encrypt = 0, passwd_if_fail = 0, passwd_req = 0;
+int checksum_required = 0, checksum_ignored = 0;
 
-main(argc, argv)
+int stripdomain = 1;
+int maxhostlen = 0;
+int always_ip = 0;
+
+int main(argc, argv)
      int argc;
      char **argv;
 {
     extern int opterr, optind;
     extern char * optarg;
-    int on = 1, fromlen, ch, i;
-    struct sockaddr_in from;
-    char *options;
+    int on = 1, ch;
+    socklen_t fromlen;
+    struct sockaddr_storage from;
     int debug_port = 0;
     int fd;
+    int do_fork = 0;
 #ifdef KERBEROS
     krb5_error_code status;
 #endif
     
     progname = *argv;
     
+    pty_init();
+    
 #ifndef LOG_NDELAY
 #define LOG_NDELAY 0
 #endif
     
-#ifdef KERBEROS
-    krb5_init_context(&bsd_context);
-    krb5_init_ets(bsd_context);
-#endif
-    
 #ifndef LOG_AUTH /* 4.2 syslog */
     openlog(progname, LOG_PID | LOG_NDELAY);
 #else
     openlog(progname, LOG_PID | LOG_NDELAY, 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, "logind")) {
-             char **newargv;
-
-             newargv = (char **) malloc(sizeof(char *) * 3);
-
-             strcpy(options, "-");
-             strncat(options, progname, i);
-
-             argc = 2;
-             
-             newargv[0] = argv[0];
-             newargv[1] = options;
-             newargv[2] = NULL;
-
-             argv = newargv;
-             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. */
     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
 #ifdef CRYPT
        case 'x':         /* Use encryption. */
        case 'X':
@@ -396,7 +428,7 @@ main(argc, argv)
          break;
 #endif
        case 'S':
-         if (status = krb5_kt_resolve(bsd_context, optarg, &keytab)) {
+         if ((status = krb5_kt_resolve(bsd_context, optarg, &keytab))) {
                  com_err(progname, status, "while resolving srvtab file %s",
                          optarg);
                  exit(2);
@@ -416,9 +448,32 @@ main(argc, argv)
          debug_port = atoi(optarg);
          break;
        case 'L':
-#ifndef DO_NOT_USE_K_LOGIN
          login_program = optarg;
-#endif
+         break;
+        case 'f':
+         do_fork = 1;
+         break;
+       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:
@@ -431,56 +486,99 @@ 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, (struct sockaddr *) &from, &fromlen)) < 0) {
-            fprintf(stderr, "Error in accept: %s\n", strerror(errno));
-            exit(2);
-        }
-        
-        close(s);
-     } 
-     else {
-        if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
-            syslog(LOG_ERR,"Can't get peer name of remote host: %m");
+    if (debug_port || do_fork) {
+       int s;
+       struct servent *ent;
+       struct sockaddr_in sock_in;
+
+       if (!debug_port) {
+           if (do_encrypt) {
+               ent = getservbyname("eklogin", "tcp");
+               if (ent == NULL)
+                   debug_port = 2105;
+               else
+                   debug_port = ent->s_port;
+           } else {
+               ent = getservbyname("klogin", "tcp");
+               if (ent == NULL)
+                   debug_port = 543;
+               else
+                   debug_port = ent->s_port;
+           }
+       }
+       if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
+           fprintf(stderr, "Error in socket: %s\n", strerror(errno));
+           exit(2);
+       }
+       memset((char *) &sock_in, 0,sizeof(sock_in));
+       sock_in.sin_family = AF_INET;
+       sock_in.sin_port = htons(debug_port);
+       sock_in.sin_addr.s_addr = INADDR_ANY;
+
+       if (!do_fork)
+           (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                             (char *)&on, sizeof(on));
+
+       if ((bind(s, (struct sockaddr *) &sock_in, sizeof(sock_in))) < 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 (do_fork) {
+           if (daemon(0, 0)) {
+               fprintf(stderr, "daemon() failed\n");
+               exit(2);
+           }
+           while (1) {
+               int child_pid;
+
+               fd = accept(s, (struct sockaddr *) &from, &fromlen);
+               if (s < 0) {
+                   if (errno != EINTR)
+                       syslog(LOG_ERR, "accept: %s", error_message(errno));
+                   continue;
+               }
+               child_pid = fork();
+               switch (child_pid) {
+               case -1:
+                   syslog(LOG_ERR, "fork: %s", error_message(errno));
+               case 0:
+                   (void) close(s);
+                   doit(fd, (struct sockaddr *) &from);
+                   close(fd);
+                   exit(0);
+               default:
+                   wait(0);
+                   close(fd);
+               }
+           }
+       }
+
+       if ((fd = accept(s, (struct sockaddr *) &from, &fromlen)) < 0) {
+           fprintf(stderr, "Error in accept: %s\n", strerror(errno));
+           exit(2);
+       }
+
+       close(s);
+    } else {                   /* !do_fork && !debug_port */
+       if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+           syslog(LOG_ERR,"Can't get peer name of remote host: %m");
 #ifdef STDERR_FILENO
-            fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
+           fatal(STDERR_FILENO, "Can't get peer name of remote host");
 #else
-            fatal(3, "Can't get peer name of remote host", 1);
+           fatal(2, "Can't get peer name of remote host");
 #endif
-        }
-        fd = 0;
-     }
+       }
+       fd = 0;
+    }
 
-    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
-      syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
-    
-    doit(fd, &from);
+    doit(fd, (struct sockaddr *) &from);
+    return 0;
 }
 
 
@@ -501,19 +599,35 @@ struct winsize win = { 0, 0, 0, 0 };
 int pid; /* child process id */
 
 void doit(f, fromp)
-     int f;
-     struct sockaddr_in *fromp;
+  int f;
+  struct sockaddr *fromp;
 {
-    int i, p, t, vfd, on = 1;
-    register struct hostent *hp;
+    int p, t, on = 1;
     char c;
+    char hname[NI_MAXHOST];
     char buferror[255];
     struct passwd *pwd;
 #ifdef POSIX_SIGNALS
     struct sigaction sa;
 #endif
-    
+    int retval;
+    char *rhost_sane;
+    int syncpipe[2];
+
     netf = -1;
+    if (setsockopt(f, SOL_SOCKET, SO_KEEPALIVE,
+                  (const char *) &on, sizeof (on)) < 0)
+       syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+    if (auth_ok == 0) {
+       syslog(LOG_CRIT, "No authentication systems were enabled; all connections will be refused.");
+       fatal(f, "All authentication systems disabled; connection refused.");
+    }
+
+    if (checksum_required&&checksum_ignored) {
+       syslog( LOG_CRIT, "Checksums are required and ignored; these options are mutually exclusive--check the documentation.");
+       fatal(f, "Configuration error: mutually exclusive options specified");
+    }
+    
     alarm(60);
     read(f, &c, 1);
     
@@ -522,6 +636,10 @@ void doit(f, fromp)
     }
 
     alarm(0);
+    /* Initialize syncpipe */
+    if (pipe( syncpipe ) < 0 )
+       fatalperror ( f , "");
+    
 
 #ifdef POSIX_SIGNALS
     /* Initialize "sa" structure. */
@@ -529,34 +647,25 @@ void doit(f, fromp)
     sa.sa_flags = 0;
 #endif
 
-    fromp->sin_port = ntohs((u_short)fromp->sin_port);
-    hp = gethostbyaddr((char *) &fromp->sin_addr, sizeof (struct in_addr),
-                      fromp->sin_family);
-    if (hp == 0) {
-       /*
-        * Only the name is used below.
-        */
-       sprintf(rhost_name,"%s",inet_ntoa(fromp->sin_addr));
-    }
-    
-    /* Save hostent information.... */
-    else strcpy(rhost_name,hp->h_name);
+    retval = getnameinfo(fromp, socklen(fromp), hname, sizeof(hname), 0, 0,
+                        NI_NUMERICHOST);
+    if (retval)
+       fatal(f, gai_strerror(retval));
+    strncpy(rhost_addra, hname, sizeof(rhost_addra));
+    rhost_addra[sizeof (rhost_addra) -1] = '\0';
     
+    retval = getnameinfo(fromp, socklen(fromp), hname, sizeof(hname), 0, 0, 0);
+    if (retval)
+       fatal(f, gai_strerror(retval));
+    strncpy(rhost_name, hname, sizeof(rhost_name));
+    rhost_name[sizeof (rhost_name) - 1] = '\0';
+
+#ifndef KERBEROS
     if (fromp->sin_family != AF_INET)
+       /* Not a real problem, we just haven't bothered to update
+          the port number checking code to handle ipv6.  */
       fatal(f, "Permission denied - Malformed from address\n");
     
-#ifdef KERBEROS
-    if (must_pass_k5 || must_pass_one) {
-       /* setup des buffers */
-       desinbuf.data = des_inbuf;
-       desoutbuf.data = des_outbuf;    /* Set up des buffers */
-    }
-    /* Must come from privileged port when .rhosts is being looked into */
-    if ((must_pass_rhosts || must_pass_one) 
-       && (fromp->sin_port >= IPPORT_RESERVED ||
-           fromp->sin_port < IPPORT_RESERVED/2))
-      non_privileged = 1;
-#else /* !KERBEROS */
     if (fromp->sin_port >= IPPORT_RESERVED ||
        fromp->sin_port < IPPORT_RESERVED/2)
       fatal(f, "Permission denied - Connection from bad port");
@@ -568,85 +677,25 @@ void doit(f, fromp)
     
 #if defined(KERBEROS)
     /* All validation, and authorization goes through do_krb_login() */
-    do_krb_login(rhost_name);
+    do_krb_login(rhost_addra, rhost_name);
 #else
     getstr(f, rusername, sizeof(rusername), "remuser");
     getstr(f, lusername, sizeof(lusername), "locuser");
     getstr(f, term, sizeof(term), "Terminal type");
+    rcmd_stream_init_normal();
 #endif
     
     write(f, "", 1);
-    if (getpty(&p,line))
-      fatal(f, "Out of ptys");
+    if ((retval = pty_getpty(&p,line, sizeof(line)))) {
+       com_err(progname, retval, "while getting master pty");
+       exit(2);
+    }
+    
     Pfd = p;
 #ifdef TIOCSWINSZ
     (void) ioctl(p, TIOCSWINSZ, &win);
 #endif
     
-#ifdef VHANG_FIRST
-    vfd = open(line, O_RDWR);
-    if (vfd < 0)
-      fatalperror(f, line);
-#ifdef NOFCHMOD
-    if (chmod(line, 0))
-      fatalperror(f, line);
-#else
-    if (fchmod(vfd, 0))
-      fatalperror(f, line);
-#endif
-    if (f == 0) {  /* if operating standalone, do not reset tty!! */
-#ifdef POSIX_SIGNALS
-       sa.sa_handler = SIG_IGN;
-       (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
-       vhangup();
-       sa.sa_handler = SIG_DFL;
-       (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
-#else
-       signal(SIGHUP, SIG_IGN);
-       vhangup();
-       signal(SIGHUP, SIG_DFL);
-#endif
-    }
-#endif /* VHANG_FIRST */
-
-#ifdef TIOCNOTTY
-      {
-        int con_fd;
-        /* Void tty association first */
-        if ((con_fd = open("/dev/tty", O_RDWR)) >= 0) {
-          ioctl(con_fd, TIOCNOTTY, 0);
-          close(con_fd);
-        }
-      }
-#endif
-
-#ifdef HAVE_SETSID
-    (void) setsid();
-#endif
-
-#ifdef ultrix
-    /* The Ultrix (and other BSD tty drivers) require the process group
-     * to be zero, in order to acquire the new tty as a controlling tty. */
-    (void) setpgrp(0, 0);
-#endif
-
-    t = open(line, O_RDWR);
-    if (t < 0)
-      fatalperror(f, line);
-
-#ifdef ultrix
-    setpgrp(0, getpid());
-#endif
-       
-#if defined(VHANG_FIRST) && !defined(VHANG_NO_CLOSE)
-    (void) close(vfd);
-#endif
-
-#ifdef TIOCSCTTY
-    if(ioctl(t, TIOCSCTTY, 0) < 0) /* set controlling tty */
-      fatalperror(f, "setting controlling tty");
-#endif
-
 #ifdef POSIX_SIGNALS
     sa.sa_handler = cleanup;
     (void) sigaction(SIGCHLD, &sa, (struct sigaction *)0);
@@ -664,60 +713,32 @@ void doit(f, fromp)
 #else
        struct sgttyb b;
 #endif /* POSIX_TERMIOS */
-
-#ifdef HAVE_STREAMS
-#ifdef HAVE_LINE_PUSH
-       while (ioctl (t, I_POP, 0) == 0); /*Clear out any old lined's*/
-       if (line_push(t) < 0)
-         fatalperror(f, "IPUSH");
-#else
-#ifdef sun
-       while (ioctl (t, I_POP, 0) == 0); /*Clear out any old lined's*/
-       if (ioctl(t, I_PUSH, "ptem") < 0)
-         fatalperror(f, "IPUSH-ptem");
-       if (ioctl(t, I_PUSH, "ldterm") < 0)
-         fatalperror(f, "IPUSH-ldterm");
-       if (ioctl(t, I_PUSH, "ttcompat") < 0)
-         fatalperror(f, "IPUSH-ttcompat");
-#endif /* sun */
-#endif /* HAVE_LINE_PUSH */
-#endif /* HAVE_STREAMS */
+       if ((retval = pty_open_slave(line, &t))) {
+           fatal(f, error_message(retval));
+           exit(1);
+       }
        
-       /*
-        * Under Ultrix 3.0, the pgrp of the slave pty terminal
-        * needs to be set explicitly.  Why rlogind works at all
-        * without this on 4.3BSD is a mystery.
-        */
-       close(f), close(p);
-       dup2(t, 0), dup2(t, 1), dup2(t, 2);
-       if (t > 2)
-         close(t);
-
-#ifdef GETPGRP_ONEARG
-       pid = getpgrp(getpid());
-#else
-       pid = getpgrp();
-#endif
-
-#ifdef TIOCSPGRP
-       ioctl(0, TIOCSPGRP, &pid);
-#endif
 
 #if defined(POSIX_TERMIOS) && !defined(ultrix)
-       tcsetpgrp(0, pid);
-       tcgetattr(0,&new_termio);
-       new_termio.c_lflag &=  ~(ICANON|ECHO|ISIG);
-       /* so that login can read the authenticator */
-       new_termio.c_iflag &= ~(IXON|IXANY|BRKINT|INLCR|ICRNL|ISTRIP);
+       tcgetattr(t,&new_termio);
+#if !defined(USE_LOGIN_F)
+       new_termio.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
+       new_termio.c_iflag &= ~(IXON|IXANY|BRKINT|INLCR|ICRNL);
+#else
+       new_termio.c_lflag |= (ICANON|ECHO|ISIG|IEXTEN);
+       new_termio.c_oflag |= (ONLCR|OPOST);
+       new_termio.c_iflag |= (IXON|IXANY|BRKINT|INLCR|ICRNL);
+#endif /*Do we need binary stream?*/
+       new_termio.c_iflag &= ~(ISTRIP);
        /* new_termio.c_iflag = 0; */
        /* new_termio.c_oflag = 0; */
        new_termio.c_cc[VMIN] = 1;
        new_termio.c_cc[VTIME] = 0;
-       tcsetattr(0,TCSANOW,&new_termio);
+       tcsetattr(t,TCSANOW,&new_termio);
 #else
-       (void)ioctl(0, TIOCGETP, &b);
+       (void)ioctl(t, TIOCGETP, &b);
        b.sg_flags = RAW|ANYP;
-       (void)ioctl(0, TIOCSETP, &b);
+       (void)ioctl(t, TIOCSETP, &b);
 #endif /* POSIX_TERMIOS */
 
        pid = 0;                        /*reset pid incase exec fails*/
@@ -730,7 +751,14 @@ void doit(f, fromp)
         **      and we don't get the right tty affiliation, and
         **      other kinds of hell breaks loose ...
         */
-       (void) write(1, &c, 1);
+       (void) write(syncpipe[1], &c, 1);
+       (void) close(syncpipe[1]);
+       (void) close(syncpipe[0]);
+               
+       close(f), close(p);
+       dup2(t, 0), dup2(t, 1), dup2(t, 2);
+       if (t > 2)
+         close(t);
        
 #if defined(sysvimp)
        setcompat (COMPAT_CLRPGROUP | (getcompat() & ~COMPAT_BSDTTY));
@@ -740,11 +768,13 @@ void doit(f, fromp)
        pwd = (struct passwd *) getpwnam(lusername);
        if (pwd && (pwd->pw_uid == 0)) {
            if (passwd_req)
-             syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) forcing password access",
-                    krusername ? krusername : "", rusername, rhost_name);
+             syslog(LOG_NOTICE, "ROOT login by %s (%s@%s (%s)) forcing password access",
+                    krusername ? krusername : "",
+                    rusername, rhost_addra, rhost_name);
            else
-             syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) ", 
-                    krusername ? krusername : "", rusername, rhost_name);
+             syslog(LOG_NOTICE, "ROOT login by %s (%s@%s (%s))", 
+                    krusername ? krusername : "",
+                    rusername, rhost_addra, rhost_name);
        }
 #ifdef KERBEROS
 #if defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
@@ -765,38 +795,54 @@ void doit(f, fromp)
        {
            if (passwd_req)
              syslog(LOG_NOTICE,
-                    "login by %s (%s@%s) as %s forcing password access\n",
+                    "login by %s (%s@%s (%s)) as %s forcing password access",
                     krusername ? krusername : "", rusername,
-                    rhost_name, lusername);
+                    rhost_addra, rhost_name, lusername);
            else 
              syslog(LOG_NOTICE,
-                    "login by %s (%s@%s) as %s\n",
+                    "login by %s (%s@%s (%s)) as %s",
                     krusername ? krusername : "", rusername,
-                    rhost_name, lusername); 
+                    rhost_addra, rhost_name, lusername); 
        }
 #endif /* LOG_REMOTE_REALM || LOG_OTHER_USERS || LOG_ALL_LOGINS */
 #endif /* KERBEROS */
 
 #ifndef NO_UT_PID
        {
-           struct utmp ent;
 
-           ent.ut_pid = getpid();
-           ent.ut_type = LOGIN_PROCESS;
-           update_utmp(&ent, "rlogin", line, ""/*host*/);
+           pty_update_utmp(PTY_LOGIN_PROCESS, getpid(), "rlogin", line,
+                           ""/*host*/, PTY_TTYSLOT_USABLE);
        }
 #endif
 
-#ifdef DO_NOT_USE_K_LOGIN
-       execl(login_program, "login", "-r", rhost_name, 0);
-#else
-       if (passwd_req)
-         execl(login_program, "login","-h", rhost_name, lusername, 0);
-       else
-         execl(login_program, "login", "-h", rhost_name, "-e", lusername, 0);
-#endif
-       
-       fatalperror(2, login_program);
+#ifdef USE_LOGIN_F
+/* use the vendors login, which has -p and -f. Tested on 
+ * AIX 4.1.4 and HPUX 10 
+ */
+    {
+        char *cp;
+        if ((cp = strchr(term,'/')))
+            *cp = '\0';
+        setenv("TERM",term, 1);
+    }
+
+    retval = pty_make_sane_hostname((struct sockaddr *) fromp, maxhostlen,
+                                   stripdomain, always_ip,
+                                   &rhost_sane);
+    if (retval)
+        fatalperror(f, "failed make_sane_hostname");
+    if (passwd_req)
+        execl(login_program, "login", "-p", "-h", rhost_sane,
+          lusername, 0);
+    else
+        execl(login_program, "login", "-p", "-h", rhost_sane,
+             "-f", lusername, 0);
+#else /* USE_LOGIN_F */
+       execl(login_program, "login", "-r", rhost_sane, 0);
+#endif /* USE_LOGIN_F */
+       syslog(LOG_ERR, "failed exec of %s: %s",
+              login_program, error_message(errno));
+       fatalperror(f, login_program);
        /*NOTREACHED*/
     } /* if (pid == 0) */
 
@@ -806,18 +852,21 @@ void doit(f, fromp)
      **      turning off echo on the slave side ...
      **      The master blocks here until it reads a byte.
      */
-    close(t);
-    if (read(p, &c, 1) != 1) {
+    
+(void) close(syncpipe[1]);
+    if (read(syncpipe[0], &c, 1) != 1) {
        /*
         * Problems read failed ...
         */
        sprintf(buferror, "Cannot read slave pty %s ",line);
        fatalperror(p,buferror);
     }
+    close(syncpipe[0]);
+
     
 #if defined(KERBEROS) 
     if (do_encrypt) {
-       if (((*des_write)(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0){
+       if (rcmd_stream_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE), 0) < 0){
            sprintf(buferror, "Cannot encrypt-write network.");
            fatal(p,buferror);
        }
@@ -834,11 +883,6 @@ void doit(f, fromp)
     /* FIONBIO doesn't always work on ptys, use fcntl to set O_NDELAY? */
     (void) fcntl(p,F_SETFL,fcntl(p,F_GETFL,0) | O_NDELAY);
 
-/*** XXX -- make this portable ***/
-#if defined(TIOCPKT) && !defined(__svr4__) || defined(solaris20)
-    ioctl(p, TIOCPKT, &on);
-#endif
-
 #ifdef POSIX_SIGNALS
     sa.sa_handler = SIG_IGN;
     (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
@@ -846,28 +890,24 @@ void doit(f, fromp)
     signal(SIGTSTP, SIG_IGN);
 #endif
 
-#ifdef hpux
-    setpgrp2(0, 0);
-#else
-    setpgrp(0, 0);
-#endif
     
-#ifdef DO_NOT_USE_K_LOGIN
+#if !defined(USE_LOGIN_F)
     /* Pass down rusername and lusername to login. */
     (void) write(p, rusername, strlen(rusername) +1);
     (void) write(p, lusername, strlen(lusername) +1);
-#endif
     /* stuff term info down to login */
-    if( write(p, term, strlen(term)+1) != strlen(term)+1 ){
+    if ((write(p, term, strlen(term)+1) != (int) strlen(term)+1)) {
        /*
         * Problems write failed ...
         */
        sprintf(buferror,"Cannot write slave pty %s ",line);
        fatalperror(f,buferror);
     }
+
+#endif
     protocol(f, p);
     signal(SIGCHLD, SIG_IGN);
-    cleanup();
+    cleanup(0);
 }
 
 unsigned char  magic[2] = { 0377, 0377 };
@@ -880,20 +920,46 @@ unsigned char     oobdata[] = {TIOCPKT_WINDOW};
 char    oobdata[] = {0};
 #endif
 
+static 
+void sendoob(fd, byte)
+     int fd;
+     char *byte;
+{
+    char message[5];
+    int cc;
+
+    if (do_inband) {
+       message[0] = '\377';
+       message[1] = '\377';
+       message[2] = 'o';
+       message[3] = 'o';
+       message[4] = *byte;
+
+       cc = rcmd_stream_write(fd, message, sizeof(message), 0);
+       while (cc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
+           /* also shouldn't happen */
+           sleep(5);
+           cc = rcmd_stream_write(fd, message, sizeof(message), 0);
+       }
+    } else {
+       send(fd, byte, 1, MSG_OOB);
+    }
+}
+
 /*
  * Handle a "control" request (signaled by magic being present)
  * in the data stream.  For now, we are only willing to handle
  * window size changes.
  */
-control(pty, cp, n)
+static int control(pty, cp, n)
      int pty;
      unsigned char *cp;
      int n;
 {
     struct winsize w;
-    int pgrp;
+    int pgrp, got_pgrp;
     
-    if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
+    if (n < (int) 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
       return (0);
 #ifdef TIOCSWINSZ
     oobdata[0] &= ~TIOCPKT_WINDOW;     /* we know he heard */
@@ -903,7 +969,13 @@ control(pty, cp, n)
     w.ws_xpixel = ntohs(w.ws_xpixel);
     w.ws_ypixel = ntohs(w.ws_ypixel);
     (void)ioctl(pty, TIOCSWINSZ, &w);
-    if (ioctl(pty, TIOCGPGRP, &pgrp) >= 0)
+#ifdef HAVE_TCGETPGRP
+    pgrp = tcgetpgrp (pty);
+    got_pgrp = pgrp != -1;
+#else
+    got_pgrp = ioctl(pty, TIOCGPGRP, &pgrp) >= 0;
+#endif
+    if (got_pgrp)
       (void) killpg(pgrp, SIGWINCH);
 #endif
     return (4+sizeof (w));
@@ -914,17 +986,30 @@ control(pty, cp, n)
 /*
  * rlogin "protocol" machine.
  */
-protocol(f, p)
+void protocol(f, p)
      int f, p;
 {
-    unsigned char pibuf[1024], fibuf[1024], *pbp, *fbp;
-    register pcc = 0, fcc = 0;
+    unsigned char pibuf[BUFSIZ], qpibuf[BUFSIZ*2], fibuf[BUFSIZ], *pbp=0, *fbp=0;
+    register int pcc = 0, fcc = 0;
     int cc;
-    char cntl;
 #ifdef POSIX_SIGNALS
     struct sigaction sa;
 #endif
+#ifdef TIOCPKT
+    register int tiocpkt_on = 0;
+    int on = 1;
+#endif
     
+#if defined(TIOCPKT) && !(defined(__svr4__) || defined(HAVE_STREAMS)) \
+       || defined(solaris20)
+    /* if system has TIOCPKT, try to turn it on. Some drivers
+     * may not support it. Save flag for later. 
+     */
+   if ( ioctl(p, TIOCPKT, &on) < 0)
+       tiocpkt_on = 0;
+   else tiocpkt_on = 1;
+#endif
+
     /*
      * Must ignore SIGTTOU, otherwise we'll stop
      * when we try and set slave pty's window shape
@@ -939,7 +1024,7 @@ protocol(f, p)
     signal(SIGTTOU, SIG_IGN);
 #endif
 #ifdef TIOCSWINSZ
-    send(f, oobdata, 1, MSG_OOB);      /* indicate new rlogin */
+    sendoob(f, oobdata);
 #endif
     for (;;) {
        fd_set ibits, obits, ebits;
@@ -952,56 +1037,47 @@ protocol(f, p)
            FD_SET(p, &obits);
        else
            FD_SET(f, &ibits);
-       if (pcc >= 0)
-           if (pcc)
+       if (pcc >= 0) {
+           if (pcc) {
                FD_SET(f, &obits);
-           else
+           } else {
                FD_SET(p, &ibits);
-       FD_SET(p, &ebits);
-       
+           }
+       }
+
        if (select(8*sizeof(ibits), &ibits, &obits, &ebits, 0) < 0) {
            if (errno == EINTR)
              continue;
            fatalperror(f, "select");
        }
 #define        pkcontrol(c)    ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
-       if (FD_ISSET(p, &ebits)) {
-           cc = read(p, &cntl, 1);
-           if (cc == 1 && pkcontrol(cntl)) {
-               cntl |= oobdata[0];
-               send(f, &cntl, 1, MSG_OOB);
-               if (cntl & TIOCPKT_FLUSHWRITE) {
-                   pcc = 0;
-                   FD_CLR(p, &ibits);
-               }
-           }
-       }
        if (FD_ISSET(f, &ibits)) {
-           fcc = (*des_read)(f, fibuf, sizeof (fibuf));
-           if (fcc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
-             fcc = 0;
-           else {
+           fcc = rcmd_stream_read(f, fibuf, sizeof (fibuf), 0);
+           if (fcc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
+               fcc = 0;
+           else {
                register unsigned char *cp;
-               int left, n;
+               int n;
+               size_t left;
                
                if (fcc <= 0)
-                 break;
+                   break;
                fbp = fibuf;
                
-             top:
-               for (cp = fibuf; cp < fibuf+fcc-1; cp++)
-                 if (cp[0] == magic[0] &&
-                     cp[1] == magic[1]) {
-                     left = fcc - (cp-fibuf);
-                     n = control(p, cp, left);
-                     if (n) {
-                         left -= n;
-                         if (left > 0)
-                           memmove(cp, cp+n, left);
-                         fcc -= n;
-                         goto top; /* n^2 */
-                     }
-                 }
+               for (cp = fibuf; cp < fibuf+fcc-1; cp++) {
+                   if (cp[0] == magic[0] &&
+                       cp[1] == magic[1]) {
+                       left = (fibuf+fcc) - cp;
+                       n = control(p, cp, left);
+                       if (n) {
+                           left -= n;
+                           fcc -= n;
+                           if (left > 0)
+                               memmove(cp, cp+n, left);
+                           cp--;
+                       }
+                   }
+               }
            }
        }
        
@@ -1016,24 +1092,56 @@ protocol(f, p)
        if (FD_ISSET(p, &ibits)) {
            pcc = read(p, pibuf, sizeof (pibuf));
            pbp = pibuf;
-           if (pcc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
-             pcc = 0;
-           else if (pcc <= 0)
-             break;
-           else if (pibuf[0] == 0)
-             pbp++, pcc--;
-#ifndef sun
-           else {
-               if (pkcontrol(pibuf[0])) {
-                   pibuf[0] |= oobdata[0];
-                   send(f, &pibuf[0], 1, MSG_OOB);
-               }
+           if (pcc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
                pcc = 0;
+           } else if (pcc <= 0) {
+               break;
+           }
+#ifdef TIOCPKT
+           else if (tiocpkt_on) {
+               if (pibuf[0] == 0) {
+                   pbp++, pcc--;
+               } else {
+                   if (pkcontrol(pibuf[0])) {
+                       pibuf[0] |= oobdata[0];
+                       sendoob(f, pibuf);
+                   }
+                   pcc = 0;
+               }
            }
 #endif
+
+           /* quote any double-\377's if necessary */
+
+           if (do_inband) {
+               unsigned char *qpbp;
+               int qpcc, i;
+
+               qpbp = qpibuf;
+               qpcc = 0;
+
+               for (i=0; i<pcc;) {
+                   if (pbp[i] == 0377u && (i+1)<pcc && pbp[i+1] == 0377u) {
+                       qpbp[qpcc] = '\377';
+                       qpbp[qpcc+1] = '\377';
+                       qpbp[qpcc+2] = 'q';
+                       qpbp[qpcc+3] = 'q';
+                       i += 2;
+                       qpcc += 4;
+                   } else {
+                       qpbp[qpcc] = pbp[i];
+                       i++;
+                       qpcc++;
+                   }
+               }
+
+               pbp = qpbp;
+               pcc = qpcc;
+           }
        }
+
        if (FD_ISSET(f, &obits) && pcc > 0) {
-           cc = (*des_write)(f, pbp, pcc);
+           cc = rcmd_stream_write(f, pbp, pcc, 0);
            if (cc < 0 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
                /* also shouldn't happen */
                sleep(5);
@@ -1049,35 +1157,20 @@ protocol(f, p)
 
 
 
-krb5_sigtype cleanup()
+krb5_sigtype cleanup(signumber)
+    int signumber;
 {
-    struct utmp ut;
-    
-#ifndef NO_UT_PID
-    ut.ut_pid = 0;
-    ut.ut_type = DEAD_PROCESS;
-#endif
-    update_utmp(&ut, "", line, (char *)0);
-    
-    (void)chmod(line, 0666);
-    (void)chown(line, 0, 0);
-#ifndef HAVE_STREAMS
-    line[strlen("/dev/")] = 'p';
-    (void)chmod(line, 0666);
-    (void)chown(line, 0, 0);
-#endif
-#ifdef VHANG_LAST
-    close(Pfd);
-    vhangup();
-#endif
+    pty_cleanup (line, pid, 1);
     shutdown(netf, 2);
+    if (ccache)
+       krb5_cc_destroy(bsd_context, ccache);
     exit(1);
 }
 
 
 void fatal(f, msg)
      int f;
-     char *msg;
+     const char *msg;
 {
     char buf[512];
     int out = 1 ;          /* Output queue of f */
@@ -1088,7 +1181,7 @@ void fatal(f, msg)
     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);
@@ -1107,7 +1200,7 @@ void fatal(f, msg)
 #else
        (void) ioctl(f, TCFLSH, out);
 #endif
-       cleanup();
+       cleanup(0);
     }
     exit(1);
 }
@@ -1116,31 +1209,23 @@ void fatal(f, msg)
 
 void fatalperror(f, msg)
      int f;
-     char *msg;
+     const char *msg;
 {
     char buf[512];
-    extern int sys_nerr;
-    extern char *sys_errlist[];
     
-    if ((unsigned)errno < sys_nerr)
-      (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
-    else
-      (void) sprintf(buf, "%s: Error %d", msg, errno);
+    (void) sprintf(buf, "%s: %s", msg, error_message(errno));
     fatal(f, buf);
 }
 
 #ifdef KERBEROS
 
 void
-do_krb_login(host)
-     char *host;
+do_krb_login(host_addr, hostname)
+     char *host_addr, *hostname;
 {
     krb5_error_code status;
-    struct passwd *pwd;
-    int        passed_krb, passed_rhosts;
-    char *msg_fail;
-
-    passed_krb = passed_rhosts = 0;
+    char *msg_fail = NULL;
+    int valid_checksum;
 
     if (getuid()) {
        exit(1);
@@ -1148,13 +1233,13 @@ do_krb_login(host)
 
     /* Check authentication. This can be either Kerberos V5, */
     /* Kerberos V4, or host-based. */
-    if (status = recvauth()) {
+    if ((status = recvauth(&valid_checksum))) {
        if (ticket)
          krb5_free_ticket(bsd_context, ticket);
        if (status != 255)
          syslog(LOG_ERR,
-                "Authentication failed from %s: %s\n",
-                host,error_message(status));
+                "Authentication failed from %s (%s): %s\n",host_addr,
+                hostname,error_message(status));
        fatal(netf, "Kerberos authentication failed");
        return;
     }
@@ -1168,59 +1253,61 @@ do_krb_login(host)
   }
 #endif
     
-    if (must_pass_k5 || must_pass_one) {
+
 #if (defined(ALWAYS_V5_KUSEROK) || !defined(KRB5_KRB4_COMPAT))
        /* krb5_kuserok returns 1 if OK */
        if (client && krb5_kuserok(bsd_context, client, lusername))
-           passed_krb++;
+         auth_sent |= ((auth_sys == KRB5_RECVAUTH_V4)?AUTH_KRB4:AUTH_KRB5);
 #else
        if (auth_sys == KRB5_RECVAUTH_V4) {
            /* kuserok returns 0 if OK */
            if (!kuserok(v4_kdata, lusername))
-               passed_krb++;
+             auth_sent |= AUTH_KRB4;
        } else {
            /* krb5_kuserok returns 1 if OK */
            if (client && krb5_kuserok(bsd_context, client, lusername))
-               passed_krb++;
+             auth_sent |= AUTH_KRB5;
        }
 #endif
-    }
-    
-    /*  The kerberos authenticated request must pass ruserok also
-       if asked for. */
+
     
-    if (!must_pass_k5 &&
-       (must_pass_rhosts || (!passed_krb && must_pass_one))) {
-       /* Cannot check .rhosts unless connection from a privileged port. */
-       if (non_privileged) 
-         fatal(netf, "Permission denied - Connection from bad port");
 
-       pwd = (struct passwd *) getpwnam(lusername);
-       if (pwd &&
-           !ruserok(rhost_name, pwd->pw_uid == 0, rusername, lusername))
-           passed_rhosts++;
+    if (checksum_required && !valid_checksum) {
+       if (auth_sent & AUTH_KRB5) {
+           syslog(LOG_WARNING, "Client did not supply required checksum--connection rejected.");
+       
+           fatal(netf, "You are using an old Kerberos5 without initial connection support; only newer clients are authorized.");
+       } else {
+         syslog(LOG_WARNING,
+                "Configuration error: Requiring checksums with -c is inconsistent with allowing Kerberos V4 connections.");
+       }
     }
-
-    if ((must_pass_k5 && passed_krb) ||
-       (must_pass_rhosts && passed_rhosts) ||
-       (must_pass_one && (passed_krb || passed_rhosts)))
-           return;
+    if (auth_ok&auth_sent) /* This should be bitwise.*/
+       return;
     
     if (ticket)
        krb5_free_ticket(bsd_context, ticket);
 
-    msg_fail =  (char *) malloc( strlen(krusername) + strlen(lusername) + 80 );
+    if (krusername)
+       msg_fail = (char *)malloc(strlen(krusername) + strlen(lusername) + 80);
     if (!msg_fail)
        fatal(netf, "User is not authorized to login to specified account");
-    sprintf(msg_fail, "User %s is not authorized to login to account %s",
-           krusername, lusername);
+
+    if (auth_sent)
+       sprintf(msg_fail, "Access denied because of improper credentials");
+    else
+       sprintf(msg_fail, "User %s is not authorized to login to account %s",
+               krusername, lusername);
+    
     fatal(netf, msg_fail);
     /* NOTREACHED */
 }
 
+#endif /* KERBEROS */
+
 
 
-getstr(fd, buf, cnt, err)
+void getstr(fd, buf, cnt, err)
      int fd;
      char *buf;
      int cnt;
@@ -1243,288 +1330,20 @@ getstr(fd, buf, cnt, err)
 
 
 
-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 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 =
-       (((krb5_ui_4)len_buf[0]<<24) |
-        ((krb5_ui_4)len_buf[1]<<16) |
-        ((krb5_ui_4)len_buf[2]<<8) |
-        (krb5_ui_4)len_buf[3]);
-#else
-       {
-           unsigned char c;
-           int gotzero = 0;
-
-           /* See the comment in v4_des_read. */
-           do {
-               cc = krb5_net_read(bsd_context, fd, &c, 1);
-               /* we should check for non-blocking here, but we'd have
-                  to make it save partial reads as well. */
-               if (cc < 0) return 0; /* read error */
-               if (cc == 1) {
-                   if (c == 0) gotzero = 1;
-               }
-           } while (!gotzero);
-
-           if ((cc = krb5_net_read(bsd_context, fd, &c, 1)) != 1) return 0;
-           rd_len = c;
-           if ((cc = krb5_net_read(bsd_context, fd, &c, 1)) != 1) return 0;
-           rd_len = (rd_len << 8) | c;
-           if ((cc = krb5_net_read(bsd_context, fd, &c, 1)) != 1) return 0;
-           rd_len = (rd_len << 8) | c;
-       }
-#endif
-    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.");
-       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++;
-           sleep(1);
-           if (retry > MAXRETRIES){
-               syslog(LOG_ERR,
-                      "des_read retry count exceeded %d\n",
-                      retry);
-               return(0);
-           }
-           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.");
-       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);
-}
-#endif /* KERBEROS */
-
-
-
-getpty(fd,slave)
-     int *fd;
-     char *slave;
-{
-    char c;
-    char *p;
-    int i,ptynum;
-    struct stat stb;
-
-#ifdef HAVE_OPENPTY
-    int slavefd;
-
-    if(openpty(fd, &slavefd, slave, (struct termios *) 0,
-         (struct winsize *) 0)) return 1;
-    return 0;
-#else
-
-    *fd = open("/dev/ptmx", O_RDWR|O_NDELAY);  /* Solaris, IRIX */
-    if (*fd < 0) *fd = open("/dev/ptc", O_RDWR|O_NDELAY); /* AIX */
-    if (*fd < 0) *fd = open("/dev/pty", O_RDWR|O_NDELAY); /* sysvimp */
-
-    if (*fd >= 0) {
-
-#ifdef HAVE_GRANTPT
-       if (grantpt(*fd) || unlockpt(*fd)) return 1;
-#endif
-    
-#ifdef HAVE_TTYNAME
-       p = ttyname(*fd);
-#else
-#ifdef HAVE_PTSNAME
-       p = ptsname(*fd);
-#else
-       /* XXX If we don't have either what do we do */
-#endif
-#endif
-       if (p) {
-           strcpy(slave, p);
-           return 0;
-       }
-
-       if (fstat(*fd, &stb) < 0) {
-           close(*fd);
-           return 1;
-       }
-       ptynum = (int)(stb.st_rdev&0xFF);
-       sprintf(slave, "/dev/ttyp%x", ptynum);
-       return 0;
-
-    } else {
-    
-       for (c = 'p'; c <= 's'; c++) {
-           sprintf(slave,"/dev/ptyXX");
-           slave[strlen("/dev/pty")] = c;
-           slave[strlen("/dev/ptyp")] = '0';
-           if (stat(slave, &stb) < 0)
-               break;
-           for (i = 0; i < 16; i++) {
-               slave[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
-               *fd = open(slave, O_RDWR);
-               if (*fd < 0) continue;
-
-               /* got pty */
-               slave[strlen("/dev/")] = 't';
-               return 0;
-           }
-       }
-       return 1;
-    }
-#endif /* HAVE_OPENPTY */
-}
-
-
-
 void usage()
 {
 #ifdef KERBEROS
     syslog(LOG_ERR, 
-          "usage: klogind [-rRkKxpP] [-D port] or [r/R][k/K][x/e][p/P]logind");
+          "usage: klogind [-ke45pPf] [-D port] [-w[ip|maxhostlen[,[no]striplocal]]] or [r/R][k/K][x/e][p/P]logind");
 #else
     syslog(LOG_ERR, 
-          "usage: rlogind [-rRpP] [-D port] or [r/R][p/P]logind");
+          "usage: rlogind [-rpPf] [-D port] or [r/R][p/P]logind");
 #endif
 }
 
 
 
 #ifdef KERBEROS
-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;
-}
-
 
 #ifndef KRB_SENDAUTH_VLEN
 #define        KRB_SENDAUTH_VLEN 8         /* length for version strings */
@@ -1534,18 +1353,24 @@ int default_realm(principal)
                                              chars */
 
 krb5_error_code
-recvauth()
+recvauth(valid_checksum)
+    int *valid_checksum;
 {
     krb5_auth_context auth_context = NULL;
     krb5_error_code status;
-    struct sockaddr_in peersin, laddr;
-    char krb_vers[KRB_SENDAUTH_VLEN + 1];
-    int len;
-    krb5_principal server;
+    struct sockaddr_storage peersin, laddr;
+    socklen_t len;
     krb5_data inbuf;
+#ifdef KRB5_KRB4_COMPAT
     char v4_instance[INST_SZ]; /* V4 Instance */
-    char v4_version[9];
+#endif
+    krb5_data version;
+    krb5_authenticator *authenticator;
+    krb5_rcache rcache;
+    enum kcmd_proto kcmd_proto;
+    krb5_keyblock *key;
 
+    *valid_checksum = 0;
     len = sizeof(laddr);
     if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
            exit(1);
@@ -1557,40 +1382,62 @@ recvauth()
        exit(1);
     }
 
-    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
 
-    if (status = krb5_auth_con_init(bsd_context, &auth_context))
+    if ((status = krb5_auth_con_init(bsd_context, &auth_context)))
         return status;
  
     /* Only need remote address for rd_cred() to verify client */
-    if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
-                       KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))
+    if ((status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf,
+                KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)))
        return status;
 
-    if (status = krb5_compat_recvauth(bsd_context, &auth_context, &netf,
-                                 "KCMDV0.1",
-                                 server,       /* Specify daemon principal */
+    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_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,
+                                              &netf,
+                                 NULL,         /* Specify daemon principal */
                                  0,            /* no flags */
                                  keytab, /* normally NULL to use v5srvtab */
 
                                  do_encrypt ? KOPT_DO_MUTUAL : 0, /*v4_opts*/
                                  "rcmd",       /* v4_service */
                                  v4_instance,  /* v4_instance */
-                                 &peersin,     /* foriegn address */
-                                 &laddr,       /* our local address */
+                                 ss2sin(&peersin), /* foriegn address */
+                                 ss2sin(&laddr), /* our local address */
                                  "",           /* use default srvtab */
 
                                  &ticket,      /* return ticket */
                                  &auth_sys,    /* which authentication system*/
-                                 &v4_kdata, v4_schedule, v4_version)) {
-
+                                 &v4_kdata, v4_schedule,
+                                              &version);
+#else
+    auth_sys = KRB5_RECVAUTH_V5;
+    status = krb5_recvauth_version(bsd_context, &auth_context, &netf,
+                                  NULL, 0, keytab, &ticket, &version);
+#endif
+    if (status) {
        if (auth_sys == KRB5_RECVAUTH_V5) {
            /*
             * clean up before exiting
@@ -1605,231 +1452,124 @@ recvauth()
     getstr(netf, lusername, sizeof (lusername), "locuser");
     getstr(netf, term, sizeof(term), "Terminal type");
 
+    kcmd_proto = KCMD_UNKNOWN_PROTOCOL;
+    if (auth_sys == KRB5_RECVAUTH_V5) {
+       if (version.length != 9) {
+           fatal (netf, "bad application version length");
+       }
+       if (!memcmp (version.data, "KCMDV0.1", 9))
+           kcmd_proto = KCMD_OLD_PROTOCOL;
+       else if (!memcmp (version.data, "KCMDV0.2", 9))
+           kcmd_proto = KCMD_NEW_PROTOCOL;
+    }
+#ifdef KRB5_KRB4_COMPAT
+    if (auth_sys == KRB5_RECVAUTH_V4)
+       kcmd_proto = KCMD_V4_PROTOCOL;
+#endif
+
+    if ((auth_sys == KRB5_RECVAUTH_V5)
+       && !(checksum_ignored
+            && kcmd_proto == KCMD_OLD_PROTOCOL)) {
+      
+      if ((status = krb5_auth_con_getauthenticator(bsd_context, auth_context,
+                                                  &authenticator)))
+       return status;
+    
+      if (authenticator->checksum) {
+       struct sockaddr_in adr;
+       socklen_t adr_length = sizeof(adr);
+       char * chksumbuf = (char *) malloc(strlen(term)+strlen(lusername)+32);
+       if (getsockname(netf, (struct sockaddr *) &adr, &adr_length) != 0)
+           goto error_cleanup;
+       if (chksumbuf == 0)
+           goto error_cleanup;
+
+       sprintf(chksumbuf,"%u:", ntohs(adr.sin_port));
+       strcat(chksumbuf,term);
+       strcat(chksumbuf,lusername);
+
+       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);
+    }
+
+
 #ifdef KRB5_KRB4_COMPAT
     if (auth_sys == KRB5_RECVAUTH_V4) {
 
-       des_read  = v4_des_read;
-       des_write = v4_des_write;
+       rcmd_stream_init_krb4(v4_kdata->session, do_encrypt, 1, 1);
 
        /* 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(rusername, v4_kdata->pname);
-       krusername = (char *) malloc(strlen(v4_kdata->pname) + 1 +
-                                    strlen(v4_kdata->pinst) + 1 +
-                                    strlen(v4_kdata->prealm) + 1);
-       sprintf(krusername, "%s/%s@%s", v4_kdata->pname,
-               v4_kdata->pinst, v4_kdata->prealm);
+       strncpy(rusername, v4_kdata->pname, sizeof(rusername) - 1);
+       rusername[sizeof(rusername) - 1] = '\0';
+
+       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, &krusername);
        
-       if (status = krb5_parse_name(bsd_context, krusername, &client))
-         return(status);
-       return 0;
+       return status;
     }
 #endif
 
     /* Must be V5  */
        
-    if (status = krb5_copy_principal(bsd_context, ticket->enc_part2->client, 
-                                    &client))
+    if ((status = krb5_copy_principal(bsd_context, ticket->enc_part2->client, 
+                                     &client)))
        return status;
 
-    des_read  = v5_des_read;
-    des_write = v5_des_write;
+    key = 0;
+    status = krb5_auth_con_getrecvsubkey (bsd_context, auth_context, &key);
+    if (status)
+       fatal (netf, "Server can't get session subkey");
+    if (!key && do_encrypt && kcmd_proto == KCMD_NEW_PROTOCOL)
+       fatal (netf, "No session subkey sent");
+    if (key && kcmd_proto == KCMD_OLD_PROTOCOL) {
+#ifdef HEIMDAL_FRIENDLY
+       key = 0;
+#else
+       fatal (netf, "Session subkey not permitted under old kcmd protocol");
+#endif
+    }
+    if (key == 0)
+       key = ticket->enc_part2->session;
+
+    rcmd_stream_init_krb5 (key, do_encrypt, 1, 0, kcmd_proto);
+
+    do_inband = (kcmd_proto == KCMD_NEW_PROTOCOL);
 
     getstr(netf, rusername, sizeof(rusername), "remuser");
 
-    if (status = krb5_unparse_name(bsd_context, client, &krusername))
+    if ((status = krb5_unparse_name(bsd_context, client, &krusername)))
        return status;
     
-    /* Setup up eblock if encrypted login session */
-    /* otherwise zero out session key */
-    if (do_encrypt) {
-       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 (status = krb5_read_message(bsd_context, (krb5_pointer)&netf, &inbuf))
+    if ((status = krb5_read_message(bsd_context, (krb5_pointer)&netf, &inbuf)))
        fatal(netf, "Error reading message");
 
     if ((inbuf.length) && /* Forwarding being done, read creds */
        (status = rd_and_store_for_creds(bsd_context, auth_context, &inbuf, 
-                                         ticket, lusername))) {
+                                         ticket, &ccache))) {
          fatal(netf, "Can't get forwarded credentials");
     }
     return 0;
 }
 
-
-#ifdef KRB5_KRB4_COMPAT
-
-int
-v4_des_read(fd, buf, len)
-int fd;
-register char *buf;
-int len;
-{
-       int nreturned = 0;
-       krb5_ui_4 net_len, rd_len;
-       int cc;
-       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 0
-       if ((cc = krb_net_read(fd, (char *)len_buf, 4)) != 4) {
-               /* XXX can't read enough, pipe
-                  must have closed */
-               return(0);
-       }
-       net_len = (((krb5_ui_4)len_buf[0]<<24) |
-                  ((krb5_ui_4)len_buf[1]<<16) |
-                  ((krb5_ui_4)len_buf[2]<<8) |
-                  (krb5_ui_4)len_buf[3]);
-#else
-       {
-           unsigned char c;
-           int gotzero = 0;
-
-           /* We're fetching the length which is MSB first, and the MSB
-              has to be zero unless the client is sending more than 2^24
-              (16M) bytes in a single write (which is why this code is in
-              rlogin but not rcp or rsh.) The only reasons we'd get something
-              other than zero are:
-                  -- corruption of the tcp stream (which will show up when
-                     everything else is out of sync too)
-                  -- un-caught Berkeley-style "pseudo out-of-band data" which
-                     happens any time the user hits ^C twice.
-              The latter is *very* common, as shown by an 'rlogin -x -d' 
-              using the CNS V4 rlogin.         Mark EIchin 1/95
-             */
-           do {
-               cc = krb_net_read(fd, &c, 1);
-               if (cc < 0) return 0; /* read error */
-               if (cc == 1) {
-                   if (c == 0) gotzero = 1;
-               }
-           } while (!gotzero);
-
-           if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0;
-           net_len = c;
-           if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0;
-           net_len = (net_len << 8) | c;
-           if ((cc = krb_net_read(fd, &c, 1)) != 1) return 0;
-           net_len = (net_len << 8) | c;
-       }
-
-#endif
-       if (net_len < 0 || net_len > sizeof(des_inbuf)) {
-               /* XXX preposterous length, probably out of sync.
-                  act as if pipe closed */
-               return(0);
-       }
-       /* the writer tells us how much real data we are getting, but
-          we need to read the pad bytes (8-byte boundary) */
-       rd_len = roundup(net_len, 8);
-       if ((cc = krb_net_read(fd, des_inbuf, rd_len)) != rd_len) {
-               /* XXX can't read enough, pipe
-                  must have closed */
-               return(0);
-       }
-       (void) pcbc_encrypt(des_inbuf,
-                           storage,
-                           (net_len < 8) ? 8 : net_len,
-                           v4_schedule,
-                           v4_kdata->session,
-                           DECRYPT);
-       /* 
-        * when the cleartext block is < 8 bytes, it is "right-justified"
-        * in the block, so we need to adjust the pointer to the data
-        */
-       if (net_len < 8)
-               store_ptr = storage + 8 - net_len;
-       else
-               store_ptr = storage;
-       nstored = net_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
-v4_des_write(fd, buf, len)
-int fd;
-char *buf;
-int len;
-{
-       static char garbage_buf[8];
-       unsigned char len_buf[4];
-
-       if (!do_encrypt)
-               return(write(fd, buf, len));
-
-       /* 
-        * pcbc_encrypt outputs in 8-byte (64 bit) increments
-        *
-        * it zero-fills the cleartext to 8-byte padding,
-        * so if we have cleartext of < 8 bytes, we want
-        * to insert random garbage before it so that the ciphertext
-        * differs for each transmission of the same cleartext.
-        * if len < 8 - sizeof(long), sizeof(long) bytes of random
-        * garbage should be sufficient; leave the rest as-is in the buffer.
-        * if len > 8 - sizeof(long), just garbage fill the rest.
-        */
-
-#ifdef min
-#undef min
-#endif
-#define min(a,b) ((a < b) ? a : b)
-
-       if (len < 8) {
-               krb5_random_confounder(8 - len, garbage_buf);
-               /* this "right-justifies" the data in the buffer */
-               (void) memcpy(garbage_buf + 8 - len, buf, len);
-       }
-       (void) pcbc_encrypt((len < 8) ? garbage_buf : buf,
-                           des_outbuf,
-                           (len < 8) ? 8 : len,
-                           v4_schedule,
-                           v4_kdata->session,
-                           ENCRYPT);
-
-       /* tell the other end the real amount, but send an 8-byte padded
-          packet */
-       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);
-       (void) write(fd, des_outbuf, roundup(len,8));
-       return(len);
-}
-
-#endif /* KRB5_KRB4_COMPAT */
 #endif /* KERBEROS */