From: Mark Eichin Date: Mon, 11 Nov 1996 21:16:55 +0000 (+0000) Subject: * Makefile.in, configure.in, krshd.c, v4rcp.M, v4rcp.c: added X-Git-Tag: krb5-1.0-freeze1~59 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=fc669be13321646e24944f233fd90f614d97480a;p=krb5.git * 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 --- diff --git a/src/appl/bsd/ChangeLog b/src/appl/bsd/ChangeLog index cc009a275..4ed41d806 100644 --- a/src/appl/bsd/ChangeLog +++ b/src/appl/bsd/ChangeLog @@ -1,3 +1,66 @@ +Mon Nov 11 15:00:25 1996 Mark Eichin + + * 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 + + * v4rcp.c : #include for O_* flags. + + Mon Jul 8 13:44:39 1996 Mark Eichin + + * 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 + + * 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 + + * 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 + + * 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 * 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 bf8136d37..891f7bde0 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 d8620999f..83ff86ee5 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 fdbaba8dc..156e8ffac 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 000000000..4d425a1ca --- /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 000000000..0a1ad33a0 --- /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 +#include +#include +#include +#include +#include +#include +#ifdef NEED_SYS_FCNTL_H +#include +#endif +#include + +#include +#include +#include +#include +#ifdef POSIX +#include +#endif +#include +#include +#include +#include +#ifdef KERBEROS +#include +#include +#if 0 +#include +#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 +#else +#include +#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 +#include + +/* + * 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 */