* Makefile.in, configure.in, krshd.c, v4rcp.M, v4rcp.c: added
authorMark Eichin <eichin@mit.edu>
Mon, 11 Nov 1996 21:16:55 +0000 (21:16 +0000)
committerMark Eichin <eichin@mit.edu>
Mon, 11 Nov 1996 21:16:55 +0000 (21:16 +0000)
kerberos V4 rcp -x support from Cygnus tree.
* Makefile.in, configure.in: added support for not building v4rcp
if --without-krb4 is used.

(original ChangeLogs copied as well.)

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@9370 dc483132-0cff-0310-8789-dd5450dbe970

src/appl/bsd/ChangeLog
src/appl/bsd/Makefile.in
src/appl/bsd/configure.in
src/appl/bsd/krshd.c
src/appl/bsd/v4rcp.M [new file with mode: 0644]
src/appl/bsd/v4rcp.c [new file with mode: 0644]

index cc009a2758b7e6d9dda0ed678961579b3361a9ff..4ed41d806c9dc7c33c099b0b7a04cbd156a0f27c 100644 (file)
@@ -1,3 +1,66 @@
+Mon Nov 11 15:00:25 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in, configure.in, krshd.c, v4rcp.M, v4rcp.c: added
+       kerberos V4 rcp -x support from Cygnus tree.
+       * Makefile.in, configure.in: added support for not building v4rcp
+       if --without-krb4 is used.
+       
+       Sun Aug 11 17:30:39 1996  Chris Provenzano  <proven@cygnus.com>
+
+       * v4rcp.c : #include <fcntl.h> for O_* flags.
+
+       Mon Jul  8 13:44:39 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * v4rcp.c (getdtablesize): eliminate local copy.
+       (kstream_write): eliminate srandom/random in favor of
+       krb5_random_confounder.
+       Add sys/socket.h to includes (v4 had it internal to krb.h.)
+       (utimes): clone utimes-emulation from v5 krcp.c (should perhaps be
+       changed to use AC_REPLACE_FUNC...)
+       Declare getenv.
+
+       Sat Jul  6 16:39:28 1996  Mark W. Eichin  <eichin@kitten.gen.ma.us>
+
+       * v4rcp.c (answer_auth): use inet_aton only if we have it;
+       otherwise fake it with inet_addr.
+       * configure.in: check_func for inet_aton.
+
+       Tue Jul  2 19:37:52 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * krshd.c (envinit): clarified initializations, labeled all slot
+       reservations (SAVEENVPAD, KRBPAD, ADDRPAD are lists of zeroes.)
+       Move TZ to always be slot 5, even on the cray. Added space for the
+       local and remote addresses.
+       (doit): add a getsockname to save the correct local address for
+       the child. Set KRB5LOCALADDR and KRB5REMOTEADDR to literal IP
+       addresses since the child is on the wrong side of a pipe and can't
+       recover them directly.
+       * v4rcp.c (kstream): add common "writelen" field for the length of
+       inbuf and outbuf.
+       (kstream_create_rcp_from_fd): initialze new fields.
+       (kstream_write): grab a big enough buffer (since this is called
+       with chunks that may correspond to the *filesystem* blocksize,
+       which is usually larger than BUFSIZ.) Also skip the first four
+       bytes of outbuf so that the encryption is done on an 8 byte
+       boundary (if malloc is correctly aligned, malloc+8 should also
+       be.)
+       (answer_auth): don't try to getpeername or getsockname, since
+       we're run under a pipe; just use KRB5LOCALADDR and KRB5REMOTEADDR
+       which are now provided by kshd (and fail if they are not present.)
+       This is safe because if they're wrong, it just means that the
+       mutual authentication will fail.
+
+       Thu Jun 27 23:32:41 1996  Mark Eichin  <eichin@cygnus.com>
+
+       * Makefile.in (all, clean, install, v4rcp): add v4rcp installation
+       and build rules.
+       * krshd.c: if we got a V4 connection and the command is rcp, use
+       the v4rcp backend to handle encryption.
+       * v4rcp.c: based on Cygnus CNS V4 rcp, stripped down to eliminate
+       user commands (and truncated usage message.)  Includes a fake
+       subset of the kstream interface that only handles "rcp -x" mode.
+       * v4rcp.M: new file, documents v4rcp support.
+
 Sat Nov  9 10:49:36 1996  Sam Hartman  <hartmans@mit.edu>
 
        * login.c: Re-arrange to deal with compiling without krb4 support. [148]
index bf8136d374fbcd888b2743c723350210c3c153d0..891f7bde0714a50407aab5c05292327d3964d25f 100644 (file)
@@ -1,18 +1,19 @@
 CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) $(DEFINES)
-LOCALINCLUDE=
+LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV
 SETENVSRC=@SETENVSRC@
 SETENVOBJ=@SETENVOBJ@
 
 LOGINLIBS =@LOGINLIBS@
 LIBOBJS=@LIBOBJS@
+V4RCPO=@V4RCPO@
 
 LOCAL_LIBRARIES=-lpty
 DEPLOCAL_LIBRARIES=$(TOPLIBD)/../util/pty/libpty.a
 
 SRCS= krcp.c krlogin.c krsh.c kcmd.c forward.c  $(SETENVSRC) \
-       login.c krshd.c krlogind.c
+       login.c krshd.c krlogind.c v4rcp.c
 OBJS= krcp.o krlogin.o krsh.o kcmd.o forward.o  $(SETENVOBJ) \
-       login.o krshd.o krlogind.o $(LIBOBJS)
+       login.o krshd.o krlogind.o $(V4RCPO) $(LIBOBJS)
 
 UCB_RLOGIN = @UCB_RLOGIN@
 UCB_RSH = @UCB_RSH@
@@ -25,10 +26,10 @@ BSD=        -DUCB_RLOGIN=\"$(UCB_RLOGIN)\" \
 DEFINES= $(RSH) $(BSD) $(RPROGS) \
        -DLOGIN_PROGRAM=\"$(SERVER_BINDIR)/login.krb5\" -DKPROGDIR=\"$(CLIENT_BINDIR)\"
 
-all:: rsh rcp rlogin kshd klogind login.krb5
+all:: rsh rcp rlogin kshd klogind login.krb5 $(V4RCP)
 
 clean:: 
-       $(RM) rsh rcp rlogin kshd klogind login.krb5
+       $(RM) rsh rcp rlogin kshd klogind login.krb5 v4rcp
 
 rsh: krsh.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
        $(LD) $(LDFLAGS) $(LDARGS) -o rsh krsh.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(LIBS)
@@ -36,6 +37,9 @@ rsh: krsh.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
 rcp: krcp.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
        $(LD) $(LDFLAGS) $(LDARGS) -o rcp krcp.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(LIBS)
 
+v4rcp: v4rcp.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
+       $(LD) $(LDFLAGS) $(LDARGS) -o v4rcp v4rcp.o $(SETENVOBJ) $(LIBOBJS) $(LIBS)
+
 rlogin: krlogin.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
        $(LD) $(LDFLAGS) $(LDARGS) -o rlogin krlogin.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(LIBS)
 
@@ -47,6 +51,12 @@ install::
                ${DESTDIR}$(CLIENT_MANDIR)/`echo $$f|sed '$(transform)'`.1 \
          ) || exit 1; \
        done
+       f=$(V4RCP); \
+       if test -n "$$f" ; then $(INSTALL_SETUID) $$f \
+               $(DESTDIR)$(CLIENT_BINDIR)/`echo $$f|sed '$(transform)'`; \
+       $(INSTALL_DATA) $(srcdir)/$$f.M \
+               ${DESTDIR}$(CLIENT_MANDIR)/`echo $$f|sed '$(transform)'`.1; \
+       fi
 
 kshd: krshd.o kcmd.o  forward.o $(SETENVOBJ) $(LIBOBJS) $(DEPLIBS)
        $(LD) $(LDFLAGS) $(LDARGS) -o kshd krshd.o kcmd.o  forward.o $(SETENVOBJ) $(LIBOBJS) $(LIBS)
index d8620999f0e6113c515d8c39712e9effe5cdf74f..83ff86ee5ac11dc77dc98586be9c812dca983caa 100644 (file)
@@ -48,19 +48,25 @@ withval=yes
 )dnl
 if test $withval = no; then
        AC_MSG_RESULT(no krb4 support)
+       V4RCP=
+       V4RCPO=
 else 
        AC_MSG_RESULT(Adding in krb4 support)
        LOGINLIBS="../../krb524/libkrb524.a $LOGINLIBS"
+       V4RCP=v4rcp
+       V4RCPO=v4rcp.o
 fi
 dnl
 dnl
 AC_SUBST(LOGINLIBS)
+AC_SUBST(V4RCP)
+AC_SUBST(V4RCP)
 dnl
 AC_VFORK
 AC_TYPE_MODE_T
 AC_FUNC_CHECK(strsave,AC_DEFINE(HAS_STRSAVE))
 AC_FUNC_CHECK(utimes,AC_DEFINE(HAS_UTIMES))
-AC_CHECK_FUNCS(isatty) 
+AC_CHECK_FUNCS(isatty inet_aton
 AC_FUNC_CHECK(gettosbyname,AC_DEFINE(HAVE_GETTOSBYNAME))
 AC_FUNC_CHECK(killpg,AC_DEFINE(HAVE_KILLPG))
 AC_FUNC_CHECK(initgroups,AC_DEFINE(HAVE_INITGROUPS))
index fdbaba8dcbe7514e788f996e078d63dc60d230c9..156e8ffac9e2938785a3ceeb7736d7a0e4b11829 100644 (file)
@@ -207,6 +207,7 @@ char *progname;
 
 /* 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;
 
@@ -444,25 +445,29 @@ char      shell[64] = "SHELL=";
 char    term[64] = "TERM=network";
 char   path_rest[] = RPATH;
 
+char   remote_addr[64];        /* = "KRB5REMOTEADDR=" */
+char   local_addr[64];         /* = "KRB5LOCALADDR=" */
+#define ADDRPAD 0,0            /* remoteaddr, localaddr */
+#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, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0};
-#define TZENV 5
+char    *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
 #else /* KERBEROS */
-char   *envinit[] =
-{homedir, shell, 0, username, term, 0, 0, 0, 0, 0, 0};
-#define TZENV 5
+char   *envinit[] = 
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, ADDRPAD, 0};
 #endif /* KERBEROS */
 #endif /* CRAY */
 
+#define TZENV 5
 #define PATHENV 2
 
 extern char    **environ;
@@ -541,6 +546,7 @@ void doit(f, fromp)
     char buf[RSHD_BUFSIZ], sig;
     krb5_sigtype     cleanup();
     struct sockaddr_in fromaddr;
+    struct sockaddr_in localaddr;
     int non_privileged = 0;
 #ifdef POSIX_SIGNALS
     struct sigaction sa;
@@ -561,6 +567,13 @@ void doit(f, fromp)
 #endif
 #endif /* IP_TOS */
     
+    { 
+      int sin_len = sizeof (struct sockaddr_in);
+      if (getsockname(f, &localaddr, &sin_len) < 0) {
+       perror("getsockname");
+       exit(1);
+      }
+    }
     fromaddr = *fromp;
 
 #ifdef POSIX_SIGNALS
@@ -1279,6 +1292,18 @@ if(port)
        }
     }
 
+    {
+      int i;
+      /* these two are covered by ADDRPAD */
+      sprintf(local_addr,  "KRB5LOCALADDR=%s", inet_ntoa(localaddr.sin_addr));
+      for (i = 0; envinit[i]; i++);
+      envinit[i] =local_addr;
+
+      sprintf(remote_addr, "KRB5REMOTEADDR=%s", inet_ntoa(fromp->sin_addr));
+      for (; envinit[i]; i++);
+      envinit[i] =remote_addr;
+    }
+
     /* If we do anything else, make sure there is space in the array. */
 
     for(cnt=0; cnt < num_env; cnt++) {
@@ -1324,7 +1349,11 @@ if(port)
         strcpy((char *) cmdbuf + offst, kprogdir);
        cp = copy + 3 + offst;
 
-       strcat(cmdbuf, "/rcp");
+       if (auth_sys == KRB5_RECVAUTH_V4) {
+         strcat(cmdbuf, "/v4rcp");
+       } else {
+         strcat(cmdbuf, "/rcp");
+       }
        if (stat((char *)cmdbuf + offst, &s) >= 0)
          strcat(cmdbuf, cp);
        else
diff --git a/src/appl/bsd/v4rcp.M b/src/appl/bsd/v4rcp.M
new file mode 100644 (file)
index 0000000..4d425a1
--- /dev/null
@@ -0,0 +1,53 @@
+.\" appl/bsd/v4rcp.M
+.so man1/header.doc
+.TH RCP 1 \*h
+.SH NAME
+v4rcp \- back end for Kerberos V4 rcp
+.SH SYNOPSIS
+.B v4rcp
+.I not invoked by users
+.SH DESCRIPTION
+This program is
+.B not
+for user execution. The usage message indicates this. 
+.PP
+Kerberos Version 4 
+.I rsh
+did not support encryption. In order to perform
+encrypted file transfer, the version 4
+.I rcp
+program did a second authentication, directly to the 
+.I rcp
+process at the other end. This meant that
+.I rcp
+needed to be
+.IR setuid
+to root in order to read the
+.IR krb-srvtab
+file on the remote end.
+.PP
+Rather than add this complexity into the main Kerberos 5 
+.I rcp
+the Kerberos 5 
+.I kshd
+instead detects the use of Kerberos 4 authentication, and checks the
+command for the program name
+.I rcp
+and then substitutes the full pathname of
+.I v4rcp
+instead.  Since
+.I v4rcp
+is installed
+.IR setuid
+to root, it can perform the the authentication and get the session key
+needed to encrypt the file transfer.
+.PP
+Kerberos 5 
+.I rcp 
+instead uses the encryption support built in to Kerberos 5
+.I rsh
+and
+.I kshd
+directly.
+.SH SEE ALSO
+rsh(1), rcp(1), kshd(8)
diff --git a/src/appl/bsd/v4rcp.c b/src/appl/bsd/v4rcp.c
new file mode 100644 (file)
index 0000000..0a1ad33
--- /dev/null
@@ -0,0 +1,1059 @@
+/* Stripped down Kerberos V4 rcp, for server-side use only */
+/* based on Cygnus CNS V4-96q1 src/appl/bsd/rcp.c. */
+
+/*
+ *     rcp.c
+ */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 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\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rcp.c      5.10 (Berkeley) 9/20/88";
+#endif /* not lint */
+
+/*
+ * rcp
+ */
+#ifdef KERBEROS
+#include "krb5.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#ifdef NEED_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#ifdef POSIX
+#include <stdlib.h>
+#endif
+#include <pwd.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <errno.h>
+#ifdef KERBEROS
+#include <krb.h>
+#include <krbports.h>
+#if 0
+#include <kstream.h>
+#else
+/* we don't have full kstream in v5, so fake it... */
+
+typedef struct {
+  int encrypting;
+  int read_fd, write_fd;
+  des_key_schedule *sched;
+  des_cblock *ivec;
+  /* used on the read side */
+  char *inbuf;
+  char *outbuf;
+  int writelen;
+  char* retbuf;
+  int retbuflen;
+  int retlen;
+  int returned;
+} *kstream;
+
+kstream kstream_create_rcp_from_fd(read_fd, write_fd, sched, ivec)
+     int read_fd, write_fd;
+     des_key_schedule *sched;
+     des_cblock *ivec;
+{
+  kstream tmp = (kstream)malloc(sizeof(kstream*));
+  tmp->encrypting = 1;
+  tmp->read_fd = read_fd;
+  tmp->write_fd = write_fd;
+  /* they're static in this file, so just hang on to the pointers */
+  tmp->sched = sched;
+  tmp->ivec = ivec;
+  tmp->inbuf = 0;
+  tmp->outbuf = 0;
+  tmp->writelen = 0;
+  tmp->retbuf = 0;
+  tmp->retbuflen = 0;
+  tmp->returned = 0;
+  tmp->retlen = 0;
+  return tmp;
+}
+
+kstream kstream_create_from_fd(read_fd, write_fd, sched, session)
+     int read_fd, write_fd;
+     Key_schedule *sched;
+     des_cblock *session;
+{
+  /* just set it up... */
+  kstream tmp = (kstream)malloc(sizeof(kstream*));
+  tmp->encrypting = 0;
+  tmp->read_fd = read_fd;
+  tmp->write_fd = write_fd;
+  return tmp;
+}
+
+
+/* always set to 0 here anyway */
+#define kstream_set_buffer_mode(x,y)
+
+int kstream_read(krem, buf, len)
+     kstream krem;
+     char *buf;
+     int len;
+{
+  if(krem->encrypting) {
+    /* when we get a length, we have to read the whole block. However,
+       we have to hand it to the user in the chunks they want, which 
+       may be smaller if BUFSIZ doesn't match. [the caller can deal if
+       the incoming blocks are smaller...] */
+    if (krem->returned) {
+      int remaining = krem->retlen - krem->returned;
+      int returning;
+      
+      if (remaining <= len) {
+       returning = remaining;
+      } else {
+       returning = len;
+      }
+      memcpy(buf, krem->retbuf+krem->returned, returning);
+      krem->returned += returning;
+      if (krem->returned == krem->retlen) krem->returned = 0;
+
+      return returning;
+    }
+
+    /* we need 4 bytes to get a length, and once we have that we know how
+       much to get to fill the buffer. Then we can hand back bits, or loop. */
+    {
+      int cc;
+      unsigned char clen[4];
+      unsigned int x = 0;
+      int sz, off;
+
+      cc = read(krem->read_fd, clen, 4);
+      if (cc != 4) return cc;
+      x <<= 8; x += clen[0] & 0xff;
+      x <<= 8; x += clen[1] & 0xff;
+      x <<= 8; x += clen[2] & 0xff;
+      x <<= 8; x += clen[3] & 0xff;
+      sz = (x + 7) & ~7;
+
+      if (krem->retbuflen < sz) {
+       if (krem->retbuflen == 0) 
+         krem->retbuf = (char*)malloc(sz>(BUFSIZ)?sz:(BUFSIZ));
+       else 
+         krem->retbuf = (char*)realloc(krem->retbuf, sz);
+       if(!krem->retbuf) { errno = ENOMEM; return -1; }
+       krem->retbuflen = sz>(BUFSIZ)?sz:(BUFSIZ);
+      }
+
+      /* get all of it */
+      off = 0;
+      do {
+       cc = read(krem->read_fd, krem->retbuf+off, sz-off);
+       if (cc <= 0) return cc;
+       off += cc;
+      } while (off < sz);
+      
+      /* decrypt it */
+      des_pcbc_encrypt ((des_cblock *)krem->retbuf, 
+                       (des_cblock *)krem->retbuf, 
+                       sz, *krem->sched, *krem->ivec, 
+                       DECRYPT);
+
+      /* now retbuf has sz bytes, return len or x of them to the user */
+      if (x <= len) {
+       memcpy(buf, krem->retbuf, x);
+       return x;
+      } else {
+       memcpy(buf, krem->retbuf, len);
+       /* defer the rest */
+       krem->returned = len;
+       krem->retlen = x;
+       return len;
+      }
+    }
+  } else {
+    return read(krem->read_fd, buf, len);
+  }
+}
+
+int kstream_write(krem, buf, len)
+     kstream krem;
+     char *buf;
+     int len;
+{
+  if (krem->encrypting) {
+    unsigned long x;
+    int st;
+    int outlen = (len + 7) & (~7);
+
+    if (krem->writelen < outlen) {
+      if (krem->writelen == 0) {
+       krem->inbuf = (char*)malloc(outlen);
+       krem->outbuf = (char*)malloc(outlen+8);
+      } else {
+       krem->inbuf = (char*)realloc(krem->inbuf, outlen);
+       krem->outbuf = (char*)realloc(krem->outbuf, outlen+8);
+      }
+      if(!krem->inbuf || !krem->outbuf) { errno = ENOMEM; return -1; }
+      krem->writelen = outlen;
+    }
+
+    outlen = (len + 7) & (~7);
+
+    memcpy(krem->inbuf, buf, len);
+    krb5_random_confounder(outlen-len, krem->inbuf+len);
+    buf = krem->inbuf;
+
+    x = len;
+    krem->outbuf[3+4] = x & 0xff; x >>= 8;
+    krem->outbuf[2+4] = x & 0xff; x >>= 8;
+    krem->outbuf[1+4] = x & 0xff; x >>= 8;
+    krem->outbuf[0+4] = x & 0xff; x >>= 8;
+    if (x)
+      abort ();
+    /* memset(outbuf+4+4, 0x42, BUFSIZ); */
+    st = des_pcbc_encrypt ((des_cblock *)buf, (des_cblock *)(krem->outbuf+4+4), outlen,
+                          *krem->sched, *krem->ivec, ENCRYPT);
+
+    if (st) abort();
+    return write(krem->write_fd, krem->outbuf+4, 4+outlen);
+  } else {
+    return write(krem->write_fd, buf, len);
+  }
+}
+
+/* 0 = stdin, read; 1 = stdout, write */
+#define rem 0,1
+
+#endif
+
+
+#ifdef _AUX_SOURCE
+#define vfork fork
+#endif
+#ifdef NOVFORK
+#define vfork fork
+#endif
+
+#ifdef hpux
+#define setreuid(r,e) setresuid(r,e,-1)
+#endif
+#ifdef __svr4__
+#define setreuid(r,e) setuid(r)
+#endif
+#ifndef roundup
+#define roundup(x,y) ((((x)+(y)-1)/(y))*(y))
+#endif
+
+int    sock;
+CREDENTIALS cred;
+MSG_DAT msg_data;
+struct sockaddr_in foreign, local;
+Key_schedule schedule;
+
+KTEXT_ST ticket;
+AUTH_DAT kdata;
+static des_cblock crypt_session_key;
+char   krb_realm[REALM_SZ];
+char   **save_argv(), *krb_realmofhost();
+#ifndef HAVE_STRSAVE
+static char *strsave();
+#endif
+#ifdef NOENCRYPTION
+#define        des_read        read
+#define        des_write       write
+#else /* !NOENCRYPTION */
+void   send_auth(), answer_auth();
+int    encryptflag = 0;
+#endif /* NOENCRYPTION */
+#include "rpaths.h"
+#else /* !KERBEROS */
+#define        des_read        read
+#define        des_write       write
+#endif /* KERBEROS */
+
+kstream krem;
+int    errs;
+#ifdef POSIX
+void   lostconn();
+#else
+int    lostconn();
+#endif
+int    errno;
+#ifndef __NetBSD__
+extern char    *sys_errlist[];
+#endif
+int    iamremote, targetshouldbedirectory;
+int    iamrecursive;
+int    pflag;
+int    force_net;
+struct passwd *pwd;
+int    userid;
+int    port;
+
+char   *getenv();
+
+struct buffer {
+       int     cnt;
+       char    *buf;
+} *allocbuf();
+
+#define        NULLBUF (struct buffer *) 0
+
+/*VARARGS*/
+int    error();
+
+#define        ga()            (void) kstream_write (krem, "", 1)
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       char *targ, *host, *src;
+       char *suser, *tuser, *thost;
+       int i;
+       char buf[BUFSIZ], cmd[50 + REALM_SZ];
+       char portarg[20], rcpportarg[20];
+#ifdef ATHENA
+       static char curhost[256];
+#endif /* ATHENA */
+#ifdef KERBEROS
+       char realmarg[REALM_SZ + 5];
+       long authopts;
+       char **orig_argv = save_argv(argc, argv);
+#endif /* KERBEROS */
+
+       portarg[0] = '\0';
+       rcpportarg[0] = '\0';
+       realmarg[0] = '\0';
+
+       pwd = getpwuid(userid = getuid());
+       if (pwd == 0) {
+               fprintf(stderr, "who are you?\n");
+               exit(1);
+       }
+
+#ifdef KERBEROS
+       krb_realm[0] = '\0';            /* Initially no kerberos realm set */
+#endif /* KERBEROS */
+       for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
+               (*argv)++;
+               while (**argv) switch (*(*argv)++) {
+
+                   case 'r':
+                       iamrecursive++;
+                       break;
+
+                   case 'p':           /* preserve mtimes and atimes */
+                       pflag++;
+                       break;
+
+                   case 'P':           /* Set port to use.  */
+                       port = atoi(*argv);
+                       sprintf(portarg, " -p%d", port);
+                       sprintf(rcpportarg, " -P%d", port);
+                       port = htons(port);
+                       goto next_arg;
+
+                   case 'N':
+                       /* Force use of network even on local machine.  */
+                       force_net++;
+                       break;
+
+#ifdef KERBEROS
+#ifndef NOENCRYPTION
+                   case 'x':
+                       encryptflag++;
+                       break;
+#endif
+                   case 'k':           /* Change kerberos realm */
+                       argc--, argv++;
+                       if (argc == 0) 
+                         usage();
+                       strncpy(krb_realm,*argv,REALM_SZ);
+                       sprintf(realmarg, " -k %s", krb_realm);
+                       goto next_arg;
+#endif /* KERBEROS */
+                   /* The rest of these are not for users. */
+                   case 'd':
+                       targetshouldbedirectory = 1;
+                       break;
+
+                   case 'f':           /* "from" */
+                       iamremote = 1;
+#if defined(KERBEROS) && !defined(NOENCRYPTION)
+                       if (encryptflag) {
+                               answer_auth();
+                               krem = kstream_create_rcp_from_fd (rem,
+                                                                  &schedule,
+                                                                  &crypt_session_key);
+                       } else
+                               krem = kstream_create_from_fd (rem, 0, 0);
+                       kstream_set_buffer_mode (krem, 0);
+#endif /* KERBEROS && !NOENCRYPTION */
+                       (void) response();
+                       (void) setuid(userid);
+                       source(--argc, ++argv);
+                       exit(errs);
+
+                   case 't':           /* "to" */
+                       iamremote = 1;
+#if defined(KERBEROS) && !defined(NOENCRYPTION)
+                       if (encryptflag) {
+                               answer_auth();
+                               krem = kstream_create_rcp_from_fd (rem,
+                                                                  &schedule,
+                                                                  &crypt_session_key);
+                       } else
+                               krem = kstream_create_from_fd (rem, 0, 0);
+                       kstream_set_buffer_mode (krem, 0);
+#endif /* KERBEROS && !NOENCRYPTION */
+                       (void) setuid(userid);
+                       sink(--argc, ++argv);
+                       exit(errs);
+
+                   default:
+                       usage();
+               }
+#ifdef KERBEROS
+             next_arg: ;
+#endif /* KERBEROS */
+       }
+       usage();
+}
+
+verifydir(cp)
+       char *cp;
+{
+       struct stat stb;
+
+       if (stat(cp, &stb) >= 0) {
+               if ((stb.st_mode & S_IFMT) == S_IFDIR)
+                       return;
+               errno = ENOTDIR;
+       }
+       error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
+       exit(1);
+}
+
+source(argc, argv)
+       int argc;
+       char **argv;
+{
+       char *last, *name;
+       struct stat stb;
+       static struct buffer buffer;
+       struct buffer *bp;
+       int x, readerr, f, amt;
+       off_t i;
+       char buf[BUFSIZ];
+
+       for (x = 0; x < argc; x++) {
+               name = argv[x];
+               if ((f = open(name, 0)) < 0) {
+                       error("rcp: %s: %s\n", name, sys_errlist[errno]);
+                       continue;
+               }
+               if (fstat(f, &stb) < 0)
+                       goto notreg;
+               switch (stb.st_mode&S_IFMT) {
+
+               case S_IFREG:
+                       break;
+
+               case S_IFDIR:
+                       if (iamrecursive) {
+                               (void) close(f);
+                               rsource(name, &stb);
+                               continue;
+                       }
+                       /* fall into ... */
+               default:
+notreg:
+                       (void) close(f);
+                       error("rcp: %s: not a plain file\n", name);
+                       continue;
+               }
+               last = strrchr(name, '/');
+               if (last == 0)
+                       last = name;
+               else
+                       last++;
+               if (pflag) {
+                       /*
+                        * Make it compatible with possible future
+                        * versions expecting microseconds.
+                        */
+                       (void) sprintf(buf, "T%ld 0 %ld 0\n",
+                           stb.st_mtime, stb.st_atime);
+                       kstream_write (krem, buf, strlen (buf));
+                       if (response() < 0) {
+                               (void) close(f);
+                               continue;
+                       }
+               }
+               (void) sprintf(buf, "C%04o %ld %s\n",
+                   stb.st_mode&07777, stb.st_size, last);
+               kstream_write (krem, buf, strlen (buf));
+               if (response() < 0) {
+                       (void) close(f);
+                       continue;
+               }
+               if ((bp = allocbuf(&buffer, f, BUFSIZ)) == NULLBUF) {
+                       (void) close(f);
+                       continue;
+               }
+               readerr = 0;
+               for (i = 0; i < stb.st_size; i += bp->cnt) {
+                       amt = bp->cnt;
+                       if (i + amt > stb.st_size)
+                               amt = stb.st_size - i;
+                       if (readerr == 0 && read(f, bp->buf, amt) != amt)
+                               readerr = errno;
+                       kstream_write (krem, bp->buf, amt);
+               }
+               (void) close(f);
+               if (readerr == 0)
+                       ga();
+               else
+                       error("rcp: %s: %s\n", name, sys_errlist[readerr]);
+               (void) response();
+       }
+}
+
+#ifndef USE_DIRENT_H
+#include <sys/dir.h>
+#else
+#include <dirent.h>
+#endif
+
+rsource(name, statp)
+       char *name;
+       struct stat *statp;
+{
+       DIR *d = opendir(name);
+       char *last;
+       char buf[BUFSIZ];
+       char *bufv[1];
+#ifdef USE_DIRENT_H
+       struct dirent *dp;
+#else
+       struct direct *dp;
+#endif
+
+       if (d == 0) {
+               error("rcp: %s: %s\n", name, sys_errlist[errno]);
+               return;
+       }
+       last = strrchr(name, '/');
+       if (last == 0)
+               last = name;
+       else
+               last++;
+       if (pflag) {
+               (void) sprintf(buf, "T%ld 0 %ld 0\n",
+                   statp->st_mtime, statp->st_atime);
+               kstream_write (krem, buf, strlen (buf));
+               if (response() < 0) {
+                       closedir(d);
+                       return;
+               }
+       }
+       (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
+       kstream_write (krem, buf, strlen (buf));
+       if (response() < 0) {
+               closedir(d);
+               return;
+       }
+       while (dp = readdir(d)) {
+               if (dp->d_ino == 0)
+                       continue;
+               if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+                       continue;
+               if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+                       error("%s/%s: Name too long.\n", name, dp->d_name);
+                       continue;
+               }
+               (void) sprintf(buf, "%s/%s", name, dp->d_name);
+               bufv[0] = buf;
+               source(1, bufv);
+       }
+       closedir(d);
+       kstream_write (krem, "E\n", 2);
+       (void) response();
+}
+
+response()
+{
+       char resp, c, rbuf[BUFSIZ], *cp = rbuf;
+
+       if (kstream_read (krem, &resp, 1) != 1)
+               lostconn();
+       switch (resp) {
+
+       case 0:                         /* ok */
+               return (0);
+
+       default:
+               *cp++ = resp;
+               /* fall into... */
+       case 1:                         /* error, followed by err msg */
+       case 2:                         /* fatal error, "" */
+               do {
+                       if (kstream_read (krem, &c, 1) != 1)
+                               lostconn();
+                       *cp++ = c;
+               } while (cp < &rbuf[BUFSIZ] && c != '\n');
+               if (iamremote == 0)
+                       (void) write(2, rbuf, cp - rbuf);
+               errs++;
+               if (resp == 1)
+                       return (-1);
+               exit(1);
+       }
+       /*NOTREACHED*/
+}
+
+#ifdef POSIX
+void
+#else
+int
+#endif
+lostconn()
+{
+
+       if (iamremote == 0)
+               fprintf(stderr, "rcp: lost connection\n");
+       exit(1);
+}
+
+#if !defined(HAS_UTIMES)
+#include <utime.h>
+#include <sys/time.h>
+
+/*
+ * We emulate utimes() instead of utime() as necessary because
+ * utimes() is more powerful than utime(), and rcp actually tries to
+ * set the microsecond values; we don't want to take away
+ * functionality unnecessarily.
+ */
+int utimes(file, tvp)
+const char *file;
+struct timeval *tvp;
+{
+       struct utimbuf times;
+
+       times.actime = tvp[0].tv_sec;
+       times.modtime = tvp[1].tv_sec;
+       return(utime(file, &times));
+}
+#endif
+
+sink(argc, argv)
+       int argc;
+       char **argv;
+{
+       off_t i, j;
+       char *targ, *whopp, *cp;
+       int of, mode, wrerr, exists, first, count, amt;
+       off_t size;
+       struct buffer *bp;
+       static struct buffer buffer;
+       struct stat stb;
+       int targisdir = 0;
+       int mask = umask(0);
+       char *myargv[1];
+       char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
+       int setimes = 0;
+       struct timeval tv[2];
+#define atime  tv[0]
+#define mtime  tv[1]
+#define        SCREWUP(str)    { whopp = str; goto screwup; }
+
+       if (!pflag)
+               (void) umask(mask);
+       if (argc != 1) {
+               error("rcp: ambiguous target\n");
+               exit(1);
+       }
+       targ = *argv;
+       if (targetshouldbedirectory)
+               verifydir(targ);
+       ga();
+       if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
+               targisdir = 1;
+       for (first = 1; ; first = 0) {
+               cp = cmdbuf;
+               if (kstream_read (krem, cp, 1) <= 0)
+                       return;
+               if (*cp++ == '\n')
+                       SCREWUP("unexpected '\\n'");
+               do {
+                       if (kstream_read(krem, cp, 1) != 1)
+                               SCREWUP("lost connection");
+               } while (*cp++ != '\n');
+               *cp = 0;
+               if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
+                       if (iamremote == 0)
+                               (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
+                       if (cmdbuf[0] == '\02')
+                               exit(1);
+                       errs++;
+                       continue;
+               }
+               *--cp = 0;
+               cp = cmdbuf;
+               if (*cp == 'E') {
+                       ga();
+                       return;
+               }
+
+#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
+               if (*cp == 'T') {
+                       setimes++;
+                       cp++;
+                       getnum(mtime.tv_sec);
+                       if (*cp++ != ' ')
+                               SCREWUP("mtime.sec not delimited");
+                       getnum(mtime.tv_usec);
+                       if (*cp++ != ' ')
+                               SCREWUP("mtime.usec not delimited");
+                       getnum(atime.tv_sec);
+                       if (*cp++ != ' ')
+                               SCREWUP("atime.sec not delimited");
+                       getnum(atime.tv_usec);
+                       if (*cp++ != '\0')
+                               SCREWUP("atime.usec not delimited");
+                       ga();
+                       continue;
+               }
+               if (*cp != 'C' && *cp != 'D') {
+                       /*
+                        * Check for the case "rcp remote:foo\* local:bar".
+                        * In this case, the line "No match." can be returned
+                        * by the shell before the rcp command on the remote is
+                        * executed so the ^Aerror_message convention isn't
+                        * followed.
+                        */
+                       if (first) {
+                               error("%s\n", cp);
+                               exit(1);
+                       }
+                       SCREWUP("expected control record");
+               }
+               cp++;
+               mode = 0;
+               for (; cp < cmdbuf+5; cp++) {
+                       if (*cp < '0' || *cp > '7')
+                               SCREWUP("bad mode");
+                       mode = (mode << 3) | (*cp - '0');
+               }
+               if (*cp++ != ' ')
+                       SCREWUP("mode not delimited");
+               size = 0;
+               while (isdigit(*cp))
+                       size = size * 10 + (*cp++ - '0');
+               if (*cp++ != ' ')
+                       SCREWUP("size not delimited");
+               if (targisdir)
+                       (void) sprintf(nambuf, "%s%s%s", targ,
+                           *targ ? "/" : "", cp);
+               else
+                       (void) strcpy(nambuf, targ);
+               exists = stat(nambuf, &stb) == 0;
+               if (cmdbuf[0] == 'D') {
+                       if (exists) {
+                               if ((stb.st_mode&S_IFMT) != S_IFDIR) {
+                                       errno = ENOTDIR;
+                                       goto bad;
+                               }
+                               if (pflag)
+                                       (void) chmod(nambuf, mode);
+                       } else if (mkdir(nambuf, mode) < 0)
+                               goto bad;
+                       myargv[0] = nambuf;
+                       sink(1, myargv);
+                       if (setimes) {
+                               setimes = 0;
+                               if (utimes(nambuf, tv) < 0)
+                                       error("rcp: can't set times on %s: %s\n",
+                                           nambuf, sys_errlist[errno]);
+                       }
+                       continue;
+               }
+               if ((of = open(nambuf, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) {
+       bad:
+                       error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
+                       continue;
+               }
+#ifdef NO_FCHMOD
+               if (exists && pflag)
+                       (void) chmod(nambuf, mode);
+#else
+               if (exists && pflag)
+                       (void) fchmod(of, mode);
+#endif
+               ga();
+               if ((bp = allocbuf(&buffer, of, BUFSIZ)) == NULLBUF) {
+                       (void) close(of);
+                       continue;
+               }
+               cp = bp->buf;
+               count = 0;
+               wrerr = 0;
+               for (i = 0; i < size; i += BUFSIZ) {
+                       amt = BUFSIZ;
+                       if (i + amt > size)
+                               amt = size - i;
+                       count += amt;
+                       do {
+                               j = kstream_read(krem, cp, amt);
+                               if (j <= 0) {
+                                       if (j == 0)
+                                           error("rcp: dropped connection");
+                                       else
+                                           error("rcp: %s\n",
+                                               sys_errlist[errno]);
+                                       exit(1);
+                               }
+                               amt -= j;
+                               cp += j;
+                       } while (amt > 0);
+                       if (count == bp->cnt) {
+                               if (wrerr == 0 &&
+                                   write(of, bp->buf, count) != count)
+                                       wrerr++;
+                               count = 0;
+                               cp = bp->buf;
+                       }
+               }
+               if (count != 0 && wrerr == 0 &&
+                   write(of, bp->buf, count) != count)
+                       wrerr++;
+#ifndef __SCO__
+               if (ftruncate(of, size))
+                       error("rcp: can't truncate %s: %s\n",
+                           nambuf, sys_errlist[errno]);
+#endif
+               (void) close(of);
+               (void) response();
+               if (setimes) {
+                       setimes = 0;
+                       if (utimes(nambuf, tv) < 0)
+                               error("rcp: can't set times on %s: %s\n",
+                                   nambuf, sys_errlist[errno]);
+               }                                  
+               if (wrerr)
+                       error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
+               else
+                       ga();
+       }
+screwup:
+       error("rcp: protocol screwup: %s\n", whopp);
+       exit(1);
+}
+
+struct buffer *
+allocbuf(bp, fd, blksize)
+       struct buffer *bp;
+       int fd, blksize;
+{
+       int size;
+#ifndef NOSTBLKSIZE
+       struct stat stb;
+
+       if (fstat(fd, &stb) < 0) {
+               error("rcp: fstat: %s\n", sys_errlist[errno]);
+               return (NULLBUF);
+       }
+       size = roundup(stb.st_blksize, blksize);
+       if (size == 0)
+#endif
+               size = blksize;
+       if (bp->cnt < size) {
+               if (bp->buf != 0)
+                       free(bp->buf);
+               bp->buf = (char *)malloc((unsigned) size);
+               if (bp->buf == 0) {
+                       error("rcp: malloc: out of memory\n");
+                       return (NULLBUF);
+               }
+       }
+       bp->cnt = size;
+       return (bp);
+}
+
+/*VARARGS1*/
+error(fmt, a1, a2, a3, a4, a5)
+       char *fmt;
+       int a1, a2, a3, a4, a5;
+{
+       char buf[BUFSIZ], *cp = buf;
+
+       errs++;
+       *cp++ = 1;
+       (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
+       if (krem)
+         (void) kstream_write(krem, buf, strlen(buf));
+       if (iamremote == 0)
+               (void) write(2, buf+1, strlen(buf+1));
+}
+
+usage()
+{
+  fprintf(stderr,
+"v4rcp: this program only acts as a server, and is not for user function.\n");
+  exit(1);
+}
+
+#ifdef KERBEROS
+
+char **
+save_argv(argc, argv)
+int argc;
+char **argv;
+{
+       register int i;
+
+       char **local_argv = (char **)calloc((unsigned) argc+1,
+                                           (unsigned) sizeof(char *));
+       /* allocate an extra pointer, so that it is initialized to NULL
+          and execv() will work */
+       for (i = 0; i < argc; i++)
+               local_argv[i] = strsave(argv[i]);
+       return(local_argv);
+}
+
+#ifndef HAVE_STRSAVE
+static char *
+strsave(sp)
+char *sp;
+{
+       register char *ret;
+       
+       if((ret = (char *)malloc((unsigned) strlen(sp)+1)) == NULL) {
+               fprintf(stderr, "rcp: no memory for saving args\n");
+               exit(1);
+       }
+       (void) strcpy(ret,sp);
+       return(ret);
+}
+#endif
+
+#ifndef NOENCRYPTION
+#undef rem
+#define rem 0
+
+void
+answer_auth()
+{
+       int sin_len, status;
+       long authopts = KOPT_DO_MUTUAL;
+       char instance[INST_SZ];
+       char version[9];
+       char *srvtab;
+       char *envaddr;
+
+#if 0
+       sin_len = sizeof (struct sockaddr_in);
+       if (getpeername(rem, &foreign, &sin_len) < 0) {
+               perror("getpeername");
+               exit(1);
+       }
+
+       sin_len = sizeof (struct sockaddr_in);
+       if (getsockname(rem, &local, &sin_len) < 0) {
+               perror("getsockname");
+               exit(1);
+       }
+#else
+       if (envaddr = getenv("KRB5LOCALADDR")) {
+#ifdef HAVE_INET_ATON
+         inet_aton(envaddr,  &local.sin_addr);
+#else
+         local.sin_addr.s_addr = inet_addr(envaddr);
+#endif
+         local.sin_family = AF_INET;
+         local.sin_port = 0;
+       } else {
+         fprintf(stderr, "v4rcp: couldn't get local address (KRB5LOCALADDR)\n");
+         exit(1);
+       }
+       if (envaddr = getenv("KRB5REMOTEADDR")) {
+#ifdef HAVE_INET_ATON
+         inet_aton(envaddr,  &foreign.sin_addr);
+#else
+         foreign.sin_addr.s_addr = inet_addr(envaddr);
+#endif
+         foreign.sin_family = AF_INET;
+         foreign.sin_port = 0;
+       } else {
+         fprintf(stderr, "v4rcp: couldn't get remote address (KRB5REMOTEADDR)\n");
+         exit(1);
+       }
+
+#endif
+       strcpy(instance, "*");
+
+       /* If rshd was invoked with the -s argument, it will set the
+           environment variable KRB_SRVTAB.  We use that to get the
+           srvtab file to use.  If we do use the environment variable,
+           we reset to our real user ID (which will already have been
+           set up by rsh).  Since rcp is setuid root, we would
+           otherwise have a security hole.  If we are using the normal
+           srvtab (KEYFILE in krb.h, normally set to /etc/krb-srvtab),
+           we must keep our effective uid of root, because that file
+           can only be read by root.  */
+       srvtab = (char *) getenv("KRB_SRVTAB");
+       if (srvtab == NULL)
+               srvtab = "";
+       if (*srvtab != '\0')
+               (void) setuid (userid);
+
+       if ((status = krb_recvauth(authopts, rem, &ticket, "rcmd", instance,
+                                  &foreign,
+                                  &local,
+                                  &kdata,
+                                  srvtab,
+                                  schedule,
+                                  version)) != KSUCCESS) {
+               fprintf(stderr, "krb_recvauth mutual fail: %s\n",
+                       krb_get_err_text(status));
+               exit(1);
+       }
+       memcpy(&crypt_session_key, &kdata.session, sizeof (crypt_session_key));
+       return;
+}
+#endif /* !NOENCRYPTION */
+
+#endif /* KERBEROS */