summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMark Eichin <eichin@mit.edu>1996-11-11 21:16:55 +0000
committerMark Eichin <eichin@mit.edu>1996-11-11 21:16:55 +0000
commitfc669be13321646e24944f233fd90f614d97480a (patch)
treec8a1bb374c673839d24a6b00e3ca4320293680d2 /src
parentd84d78771aef558c3bcff8dcb85c983a2a0e9735 (diff)
downloadkrb5-fc669be13321646e24944f233fd90f614d97480a.tar.gz
krb5-fc669be13321646e24944f233fd90f614d97480a.tar.xz
krb5-fc669be13321646e24944f233fd90f614d97480a.zip
* 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. (original ChangeLogs copied as well.) git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@9370 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src')
-rw-r--r--src/appl/bsd/ChangeLog63
-rw-r--r--src/appl/bsd/Makefile.in20
-rw-r--r--src/appl/bsd/configure.in8
-rw-r--r--src/appl/bsd/krshd.c51
-rw-r--r--src/appl/bsd/v4rcp.M53
-rw-r--r--src/appl/bsd/v4rcp.c1059
6 files changed, 1237 insertions, 17 deletions
diff --git a/src/appl/bsd/ChangeLog b/src/appl/bsd/ChangeLog
index cc009a2758..4ed41d806c 100644
--- a/src/appl/bsd/ChangeLog
+++ b/src/appl/bsd/ChangeLog
@@ -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]
diff --git a/src/appl/bsd/Makefile.in b/src/appl/bsd/Makefile.in
index bf8136d374..891f7bde07 100644
--- a/src/appl/bsd/Makefile.in
+++ b/src/appl/bsd/Makefile.in
@@ -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)
diff --git a/src/appl/bsd/configure.in b/src/appl/bsd/configure.in
index d8620999f0..83ff86ee5a 100644
--- a/src/appl/bsd/configure.in
+++ b/src/appl/bsd/configure.in
@@ -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))
diff --git a/src/appl/bsd/krshd.c b/src/appl/bsd/krshd.c
index fdbaba8dcb..156e8ffac9 100644
--- a/src/appl/bsd/krshd.c
+++ b/src/appl/bsd/krshd.c
@@ -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
index 0000000000..4d425a1cae
--- /dev/null
+++ b/src/appl/bsd/v4rcp.M
@@ -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
index 0000000000..0a1ad33a0e
--- /dev/null
+++ b/src/appl/bsd/v4rcp.c
@@ -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 */