1 /* Stripped down Kerberos V4 rcp, for server-side use only */
2 /* based on Cygnus CNS V4-96q1 src/appl/bsd/rcp.c. */
9 * Copyright (c) 1983 The Regents of the University of California.
10 * All rights reserved.
12 * Redistribution and use in source and binary forms are permitted
13 * provided that the above copyright notice and this paragraph are
14 * duplicated in all such forms and that any documentation,
15 * advertising materials, and other materials related to such
16 * distribution and use acknowledge that the software was developed
17 * by the University of California, Berkeley. The name of the
18 * University may not be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
28 All rights reserved.\n";
32 static char sccsid[] = "@(#)rcp.c 5.10 (Berkeley) 9/20/88";
42 #include <sys/types.h>
43 #include <sys/param.h>
45 #include <sys/socket.h>
48 #include <sys/ioctl.h>
49 #ifdef NEED_SYS_FCNTL_H
50 #include <sys/fcntl.h>
52 #include <netinet/in.h>
71 /* we don't have full kstream in v5, so fake it... */
75 int read_fd, write_fd;
76 des_key_schedule *sched;
78 /* used on the read side */
88 kstream kstream_create_rcp_from_fd(read_fd, write_fd, sched, ivec)
89 int read_fd, write_fd;
90 des_key_schedule *sched;
93 kstream tmp = (kstream)malloc(sizeof(kstream*));
95 tmp->read_fd = read_fd;
96 tmp->write_fd = write_fd;
97 /* they're static in this file, so just hang on to the pointers */
110 kstream kstream_create_from_fd(read_fd, write_fd, sched, session)
111 int read_fd, write_fd;
115 /* just set it up... */
116 kstream tmp = (kstream)malloc(sizeof(kstream*));
118 tmp->read_fd = read_fd;
119 tmp->write_fd = write_fd;
124 /* always set to 0 here anyway */
125 #define kstream_set_buffer_mode(x,y)
127 int kstream_read(krem, buf, len)
132 if(krem->encrypting) {
133 /* when we get a length, we have to read the whole block. However,
134 we have to hand it to the user in the chunks they want, which
135 may be smaller if BUFSIZ doesn't match. [the caller can deal if
136 the incoming blocks are smaller...] */
137 if (krem->returned) {
138 int remaining = krem->retlen - krem->returned;
141 if (remaining <= len) {
142 returning = remaining;
146 memcpy(buf, krem->retbuf+krem->returned, returning);
147 krem->returned += returning;
148 if (krem->returned == krem->retlen) krem->returned = 0;
153 /* we need 4 bytes to get a length, and once we have that we know how
154 much to get to fill the buffer. Then we can hand back bits, or loop. */
157 unsigned char clen[4];
161 cc = read(krem->read_fd, clen, 4);
162 if (cc != 4) return cc;
163 x <<= 8; x += clen[0] & 0xff;
164 x <<= 8; x += clen[1] & 0xff;
165 x <<= 8; x += clen[2] & 0xff;
166 x <<= 8; x += clen[3] & 0xff;
169 if (krem->retbuflen < sz) {
170 if (krem->retbuflen == 0)
171 krem->retbuf = (char*)malloc(sz>(BUFSIZ)?sz:(BUFSIZ));
173 krem->retbuf = (char*)realloc(krem->retbuf, sz);
174 if(!krem->retbuf) { errno = ENOMEM; return -1; }
175 krem->retbuflen = sz>(BUFSIZ)?sz:(BUFSIZ);
181 cc = read(krem->read_fd, krem->retbuf+off, sz-off);
182 if (cc <= 0) return cc;
187 des_pcbc_encrypt ((des_cblock *)krem->retbuf,
188 (des_cblock *)krem->retbuf,
189 sz, *krem->sched, *krem->ivec,
192 /* now retbuf has sz bytes, return len or x of them to the user */
194 memcpy(buf, krem->retbuf, x);
197 memcpy(buf, krem->retbuf, len);
199 krem->returned = len;
205 return read(krem->read_fd, buf, len);
209 int kstream_write(krem, buf, len)
214 if (krem->encrypting) {
217 int outlen = (len + 7) & (~7);
219 if (krem->writelen < outlen) {
220 if (krem->writelen == 0) {
221 krem->inbuf = (char*)malloc(outlen);
222 krem->outbuf = (char*)malloc(outlen+8);
224 krem->inbuf = (char*)realloc(krem->inbuf, outlen);
225 krem->outbuf = (char*)realloc(krem->outbuf, outlen+8);
227 if(!krem->inbuf || !krem->outbuf) { errno = ENOMEM; return -1; }
228 krem->writelen = outlen;
231 outlen = (len + 7) & (~7);
233 memcpy(krem->inbuf, buf, len);
234 krb5_random_confounder(outlen-len, krem->inbuf+len);
238 krem->outbuf[3+4] = x & 0xff; x >>= 8;
239 krem->outbuf[2+4] = x & 0xff; x >>= 8;
240 krem->outbuf[1+4] = x & 0xff; x >>= 8;
241 krem->outbuf[0+4] = x & 0xff; x >>= 8;
244 /* memset(outbuf+4+4, 0x42, BUFSIZ); */
245 st = des_pcbc_encrypt ((des_cblock *)buf, (des_cblock *)(krem->outbuf+4+4), outlen,
246 *krem->sched, *krem->ivec, ENCRYPT);
249 return write(krem->write_fd, krem->outbuf+4, 4+outlen);
251 return write(krem->write_fd, buf, len);
255 /* 0 = stdin, read; 1 = stdout, write */
269 #define setreuid(r,e) setresuid(r,e,-1)
272 #define setreuid(r,e) setuid(r)
275 #define roundup(x,y) ((((x)+(y)-1)/(y))*(y))
281 struct sockaddr_in foreign, local;
282 Key_schedule schedule;
286 static des_cblock crypt_session_key;
287 char krb_realm[REALM_SZ];
288 char **save_argv(), *krb_realmofhost();
290 static char *strsave();
293 #define des_read read
294 #define des_write write
295 #else /* !NOENCRYPTION */
296 void send_auth(), answer_auth();
298 #endif /* NOENCRYPTION */
300 #else /* !KERBEROS */
301 #define des_read read
302 #define des_write write
303 #endif /* KERBEROS */
314 extern char *sys_errlist[];
316 int iamremote, targetshouldbedirectory;
331 #define NULLBUF (struct buffer *) 0
336 #define ga() (void) kstream_write (krem, "", 1)
342 char *targ, *host, *src;
343 char *suser, *tuser, *thost;
345 char buf[BUFSIZ], cmd[50 + REALM_SZ];
346 char portarg[20], rcpportarg[20];
348 static char curhost[256];
351 char realmarg[REALM_SZ + 5];
353 char **orig_argv = save_argv(argc, argv);
354 #endif /* KERBEROS */
357 rcpportarg[0] = '\0';
360 pwd = getpwuid(userid = getuid());
362 fprintf(stderr, "who are you?\n");
367 krb_realm[0] = '\0'; /* Initially no kerberos realm set */
368 #endif /* KERBEROS */
369 for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
371 while (**argv) switch (*(*argv)++) {
377 case 'p': /* preserve mtimes and atimes */
381 case 'P': /* Set port to use. */
383 sprintf(portarg, " -p%d", port);
384 sprintf(rcpportarg, " -P%d", port);
389 /* Force use of network even on local machine. */
399 case 'k': /* Change kerberos realm */
403 strncpy(krb_realm,*argv,REALM_SZ);
404 sprintf(realmarg, " -k %s", krb_realm);
406 #endif /* KERBEROS */
407 /* The rest of these are not for users. */
409 targetshouldbedirectory = 1;
412 case 'f': /* "from" */
414 #if defined(KERBEROS) && !defined(NOENCRYPTION)
417 krem = kstream_create_rcp_from_fd (rem,
421 krem = kstream_create_from_fd (rem, 0, 0);
422 kstream_set_buffer_mode (krem, 0);
423 #endif /* KERBEROS && !NOENCRYPTION */
425 (void) setuid(userid);
426 source(--argc, ++argv);
431 #if defined(KERBEROS) && !defined(NOENCRYPTION)
434 krem = kstream_create_rcp_from_fd (rem,
438 krem = kstream_create_from_fd (rem, 0, 0);
439 kstream_set_buffer_mode (krem, 0);
440 #endif /* KERBEROS && !NOENCRYPTION */
441 (void) setuid(userid);
442 sink(--argc, ++argv);
450 #endif /* KERBEROS */
460 if (stat(cp, &stb) >= 0) {
461 if ((stb.st_mode & S_IFMT) == S_IFDIR)
465 error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
475 static struct buffer buffer;
477 int x, readerr, f, amt;
481 for (x = 0; x < argc; x++) {
483 if ((f = open(name, 0)) < 0) {
484 error("rcp: %s: %s\n", name, sys_errlist[errno]);
487 if (fstat(f, &stb) < 0)
489 switch (stb.st_mode&S_IFMT) {
504 error("rcp: %s: not a plain file\n", name);
507 last = strrchr(name, '/');
514 * Make it compatible with possible future
515 * versions expecting microseconds.
517 (void) sprintf(buf, "T%ld 0 %ld 0\n",
518 stb.st_mtime, stb.st_atime);
519 kstream_write (krem, buf, strlen (buf));
520 if (response() < 0) {
525 (void) sprintf(buf, "C%04o %ld %s\n",
526 stb.st_mode&07777, stb.st_size, last);
527 kstream_write (krem, buf, strlen (buf));
528 if (response() < 0) {
532 if ((bp = allocbuf(&buffer, f, BUFSIZ)) == NULLBUF) {
537 for (i = 0; i < stb.st_size; i += bp->cnt) {
539 if (i + amt > stb.st_size)
540 amt = stb.st_size - i;
541 if (readerr == 0 && read(f, bp->buf, amt) != amt)
543 kstream_write (krem, bp->buf, amt);
549 error("rcp: %s: %s\n", name, sys_errlist[readerr]);
564 DIR *d = opendir(name);
575 error("rcp: %s: %s\n", name, sys_errlist[errno]);
578 last = strrchr(name, '/');
584 (void) sprintf(buf, "T%ld 0 %ld 0\n",
585 statp->st_mtime, statp->st_atime);
586 kstream_write (krem, buf, strlen (buf));
587 if (response() < 0) {
592 (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
593 kstream_write (krem, buf, strlen (buf));
594 if (response() < 0) {
598 while (dp = readdir(d)) {
601 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
603 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
604 error("%s/%s: Name too long.\n", name, dp->d_name);
607 (void) sprintf(buf, "%s/%s", name, dp->d_name);
612 kstream_write (krem, "E\n", 2);
618 char resp, c, rbuf[BUFSIZ], *cp = rbuf;
620 if (kstream_read (krem, &resp, 1) != 1)
630 case 1: /* error, followed by err msg */
631 case 2: /* fatal error, "" */
633 if (kstream_read (krem, &c, 1) != 1)
636 } while (cp < &rbuf[BUFSIZ] && c != '\n');
638 (void) write(2, rbuf, cp - rbuf);
656 fprintf(stderr, "rcp: lost connection\n");
660 #if !defined(HAS_UTIMES)
662 #include <sys/time.h>
665 * We emulate utimes() instead of utime() as necessary because
666 * utimes() is more powerful than utime(), and rcp actually tries to
667 * set the microsecond values; we don't want to take away
668 * functionality unnecessarily.
670 int utimes(file, tvp)
674 struct utimbuf times;
676 times.actime = tvp[0].tv_sec;
677 times.modtime = tvp[1].tv_sec;
678 return(utime(file, ×));
687 char *targ, *whopp, *cp;
688 int of, mode, wrerr, exists, first, count, amt;
691 static struct buffer buffer;
696 char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
698 struct timeval tv[2];
701 #define SCREWUP(str) { whopp = str; goto screwup; }
706 error("rcp: ambiguous target\n");
710 if (targetshouldbedirectory)
713 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
715 for (first = 1; ; first = 0) {
717 if (kstream_read (krem, cp, 1) <= 0)
720 SCREWUP("unexpected '\\n'");
722 if (kstream_read(krem, cp, 1) != 1)
723 SCREWUP("lost connection");
724 } while (*cp++ != '\n');
726 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
728 (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
729 if (cmdbuf[0] == '\02')
741 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
745 getnum(mtime.tv_sec);
747 SCREWUP("mtime.sec not delimited");
748 getnum(mtime.tv_usec);
750 SCREWUP("mtime.usec not delimited");
751 getnum(atime.tv_sec);
753 SCREWUP("atime.sec not delimited");
754 getnum(atime.tv_usec);
756 SCREWUP("atime.usec not delimited");
760 if (*cp != 'C' && *cp != 'D') {
762 * Check for the case "rcp remote:foo\* local:bar".
763 * In this case, the line "No match." can be returned
764 * by the shell before the rcp command on the remote is
765 * executed so the ^Aerror_message convention isn't
772 SCREWUP("expected control record");
776 for (; cp < cmdbuf+5; cp++) {
777 if (*cp < '0' || *cp > '7')
779 mode = (mode << 3) | (*cp - '0');
782 SCREWUP("mode not delimited");
785 size = size * 10 + (*cp++ - '0');
787 SCREWUP("size not delimited");
789 (void) sprintf(nambuf, "%s%s%s", targ,
790 *targ ? "/" : "", cp);
792 (void) strcpy(nambuf, targ);
793 exists = stat(nambuf, &stb) == 0;
794 if (cmdbuf[0] == 'D') {
796 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
801 (void) chmod(nambuf, mode);
802 } else if (mkdir(nambuf, mode) < 0)
808 if (utimes(nambuf, tv) < 0)
809 error("rcp: can't set times on %s: %s\n",
810 nambuf, sys_errlist[errno]);
814 if ((of = open(nambuf, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) {
816 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
821 (void) chmod(nambuf, mode);
824 (void) fchmod(of, mode);
827 if ((bp = allocbuf(&buffer, of, BUFSIZ)) == NULLBUF) {
834 for (i = 0; i < size; i += BUFSIZ) {
840 j = kstream_read(krem, cp, amt);
843 error("rcp: dropped connection");
852 if (count == bp->cnt) {
854 write(of, bp->buf, count) != count)
860 if (count != 0 && wrerr == 0 &&
861 write(of, bp->buf, count) != count)
864 if (ftruncate(of, size))
865 error("rcp: can't truncate %s: %s\n",
866 nambuf, sys_errlist[errno]);
872 if (utimes(nambuf, tv) < 0)
873 error("rcp: can't set times on %s: %s\n",
874 nambuf, sys_errlist[errno]);
877 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
882 error("rcp: protocol screwup: %s\n", whopp);
887 allocbuf(bp, fd, blksize)
895 if (fstat(fd, &stb) < 0) {
896 error("rcp: fstat: %s\n", sys_errlist[errno]);
899 size = roundup(stb.st_blksize, blksize);
903 if (bp->cnt < size) {
906 bp->buf = (char *)malloc((unsigned) size);
908 error("rcp: malloc: out of memory\n");
917 error(fmt, a1, a2, a3, a4, a5)
919 int a1, a2, a3, a4, a5;
921 char buf[BUFSIZ], *cp = buf;
925 (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
927 (void) kstream_write(krem, buf, strlen(buf));
929 (void) write(2, buf+1, strlen(buf+1));
935 "v4rcp: this program only acts as a server, and is not for user function.\n");
942 save_argv(argc, argv)
948 char **local_argv = (char **)calloc((unsigned) argc+1,
949 (unsigned) sizeof(char *));
950 /* allocate an extra pointer, so that it is initialized to NULL
951 and execv() will work */
952 for (i = 0; i < argc; i++)
953 local_argv[i] = strsave(argv[i]);
964 if((ret = (char *)malloc((unsigned) strlen(sp)+1)) == NULL) {
965 fprintf(stderr, "rcp: no memory for saving args\n");
968 (void) strcpy(ret,sp);
981 long authopts = KOPT_DO_MUTUAL;
982 char instance[INST_SZ];
988 sin_len = sizeof (struct sockaddr_in);
989 if (getpeername(rem, &foreign, &sin_len) < 0) {
990 perror("getpeername");
994 sin_len = sizeof (struct sockaddr_in);
995 if (getsockname(rem, &local, &sin_len) < 0) {
996 perror("getsockname");
1000 if (envaddr = getenv("KRB5LOCALADDR")) {
1001 #ifdef HAVE_INET_ATON
1002 inet_aton(envaddr, &local.sin_addr);
1004 local.sin_addr.s_addr = inet_addr(envaddr);
1006 local.sin_family = AF_INET;
1009 fprintf(stderr, "v4rcp: couldn't get local address (KRB5LOCALADDR)\n");
1012 if (envaddr = getenv("KRB5REMOTEADDR")) {
1013 #ifdef HAVE_INET_ATON
1014 inet_aton(envaddr, &foreign.sin_addr);
1016 foreign.sin_addr.s_addr = inet_addr(envaddr);
1018 foreign.sin_family = AF_INET;
1019 foreign.sin_port = 0;
1021 fprintf(stderr, "v4rcp: couldn't get remote address (KRB5REMOTEADDR)\n");
1026 strcpy(instance, "*");
1028 /* If rshd was invoked with the -s argument, it will set the
1029 environment variable KRB_SRVTAB. We use that to get the
1030 srvtab file to use. If we do use the environment variable,
1031 we reset to our real user ID (which will already have been
1032 set up by rsh). Since rcp is setuid root, we would
1033 otherwise have a security hole. If we are using the normal
1034 srvtab (KEYFILE in krb.h, normally set to /etc/krb-srvtab),
1035 we must keep our effective uid of root, because that file
1036 can only be read by root. */
1037 srvtab = (char *) getenv("KRB_SRVTAB");
1040 if (*srvtab != '\0')
1041 (void) setuid (userid);
1043 if ((status = krb_recvauth(authopts, rem, &ticket, "rcmd", instance,
1049 version)) != KSUCCESS) {
1050 fprintf(stderr, "krb_recvauth mutual fail: %s\n",
1051 krb_get_err_text(status));
1054 memcpy(&crypt_session_key, &kdata.session, sizeof (crypt_session_key));
1057 #endif /* !NOENCRYPTION */
1059 #endif /* KERBEROS */