--- /dev/null
+/* 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, ×));
+}
+#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 */