0a1ad33a0e5c97d6bb60e70da77de9e87d461f94
[krb5.git] / src / appl / bsd / v4rcp.c
1 /* Stripped down Kerberos V4 rcp, for server-side use only */
2 /* based on Cygnus CNS V4-96q1 src/appl/bsd/rcp.c. */
3
4 /*
5  *      rcp.c
6  */
7
8 /*
9  * Copyright (c) 1983 The Regents of the University of California.
10  * All rights reserved.
11  *
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.
23  */
24
25 #ifndef lint
26 char copyright[] =
27 "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
28  All rights reserved.\n";
29 #endif /* not lint */
30
31 #ifndef lint
32 static char sccsid[] = "@(#)rcp.c       5.10 (Berkeley) 9/20/88";
33 #endif /* not lint */
34
35 /*
36  * rcp
37  */
38 #ifdef KERBEROS
39 #include "krb5.h"
40 #endif
41
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/file.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 #include <sys/time.h>
48 #include <sys/ioctl.h>
49 #ifdef NEED_SYS_FCNTL_H
50 #include <sys/fcntl.h>
51 #endif
52 #include <netinet/in.h>
53
54 #include <fcntl.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <signal.h>
58 #ifdef POSIX
59 #include <stdlib.h>
60 #endif
61 #include <pwd.h>
62 #include <ctype.h>
63 #include <netdb.h>
64 #include <errno.h>
65 #ifdef KERBEROS
66 #include <krb.h>
67 #include <krbports.h>
68 #if 0
69 #include <kstream.h>
70 #else
71 /* we don't have full kstream in v5, so fake it... */
72
73 typedef struct {
74   int encrypting;
75   int read_fd, write_fd;
76   des_key_schedule *sched;
77   des_cblock *ivec;
78   /* used on the read side */
79   char *inbuf;
80   char *outbuf;
81   int writelen;
82   char* retbuf;
83   int retbuflen;
84   int retlen;
85   int returned;
86 } *kstream;
87
88 kstream kstream_create_rcp_from_fd(read_fd, write_fd, sched, ivec)
89      int read_fd, write_fd;
90      des_key_schedule *sched;
91      des_cblock *ivec;
92 {
93   kstream tmp = (kstream)malloc(sizeof(kstream*));
94   tmp->encrypting = 1;
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 */
98   tmp->sched = sched;
99   tmp->ivec = ivec;
100   tmp->inbuf = 0;
101   tmp->outbuf = 0;
102   tmp->writelen = 0;
103   tmp->retbuf = 0;
104   tmp->retbuflen = 0;
105   tmp->returned = 0;
106   tmp->retlen = 0;
107   return tmp;
108 }
109
110 kstream kstream_create_from_fd(read_fd, write_fd, sched, session)
111      int read_fd, write_fd;
112      Key_schedule *sched;
113      des_cblock *session;
114 {
115   /* just set it up... */
116   kstream tmp = (kstream)malloc(sizeof(kstream*));
117   tmp->encrypting = 0;
118   tmp->read_fd = read_fd;
119   tmp->write_fd = write_fd;
120   return tmp;
121 }
122
123
124 /* always set to 0 here anyway */
125 #define kstream_set_buffer_mode(x,y)
126
127 int kstream_read(krem, buf, len)
128      kstream krem;
129      char *buf;
130      int len;
131 {
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;
139       int returning;
140       
141       if (remaining <= len) {
142         returning = remaining;
143       } else {
144         returning = len;
145       }
146       memcpy(buf, krem->retbuf+krem->returned, returning);
147       krem->returned += returning;
148       if (krem->returned == krem->retlen) krem->returned = 0;
149
150       return returning;
151     }
152
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. */
155     {
156       int cc;
157       unsigned char clen[4];
158       unsigned int x = 0;
159       int sz, off;
160
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;
167       sz = (x + 7) & ~7;
168
169       if (krem->retbuflen < sz) {
170         if (krem->retbuflen == 0) 
171           krem->retbuf = (char*)malloc(sz>(BUFSIZ)?sz:(BUFSIZ));
172         else 
173           krem->retbuf = (char*)realloc(krem->retbuf, sz);
174         if(!krem->retbuf) { errno = ENOMEM; return -1; }
175         krem->retbuflen = sz>(BUFSIZ)?sz:(BUFSIZ);
176       }
177
178       /* get all of it */
179       off = 0;
180       do {
181         cc = read(krem->read_fd, krem->retbuf+off, sz-off);
182         if (cc <= 0) return cc;
183         off += cc;
184       } while (off < sz);
185       
186       /* decrypt it */
187       des_pcbc_encrypt ((des_cblock *)krem->retbuf, 
188                         (des_cblock *)krem->retbuf, 
189                         sz, *krem->sched, *krem->ivec, 
190                         DECRYPT);
191
192       /* now retbuf has sz bytes, return len or x of them to the user */
193       if (x <= len) {
194         memcpy(buf, krem->retbuf, x);
195         return x;
196       } else {
197         memcpy(buf, krem->retbuf, len);
198         /* defer the rest */
199         krem->returned = len;
200         krem->retlen = x;
201         return len;
202       }
203     }
204   } else {
205     return read(krem->read_fd, buf, len);
206   }
207 }
208
209 int kstream_write(krem, buf, len)
210      kstream krem;
211      char *buf;
212      int len;
213 {
214   if (krem->encrypting) {
215     unsigned long x;
216     int st;
217     int outlen = (len + 7) & (~7);
218
219     if (krem->writelen < outlen) {
220       if (krem->writelen == 0) {
221         krem->inbuf = (char*)malloc(outlen);
222         krem->outbuf = (char*)malloc(outlen+8);
223       } else {
224         krem->inbuf = (char*)realloc(krem->inbuf, outlen);
225         krem->outbuf = (char*)realloc(krem->outbuf, outlen+8);
226       }
227       if(!krem->inbuf || !krem->outbuf) { errno = ENOMEM; return -1; }
228       krem->writelen = outlen;
229     }
230
231     outlen = (len + 7) & (~7);
232
233     memcpy(krem->inbuf, buf, len);
234     krb5_random_confounder(outlen-len, krem->inbuf+len);
235     buf = krem->inbuf;
236
237     x = 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;
242     if (x)
243       abort ();
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);
247
248     if (st) abort();
249     return write(krem->write_fd, krem->outbuf+4, 4+outlen);
250   } else {
251     return write(krem->write_fd, buf, len);
252   }
253 }
254
255 /* 0 = stdin, read; 1 = stdout, write */
256 #define rem 0,1
257
258 #endif
259
260
261 #ifdef _AUX_SOURCE
262 #define vfork fork
263 #endif
264 #ifdef NOVFORK
265 #define vfork fork
266 #endif
267
268 #ifdef hpux
269 #define setreuid(r,e) setresuid(r,e,-1)
270 #endif
271 #ifdef __svr4__
272 #define setreuid(r,e) setuid(r)
273 #endif
274 #ifndef roundup
275 #define roundup(x,y) ((((x)+(y)-1)/(y))*(y))
276 #endif
277
278 int     sock;
279 CREDENTIALS cred;
280 MSG_DAT msg_data;
281 struct sockaddr_in foreign, local;
282 Key_schedule schedule;
283
284 KTEXT_ST ticket;
285 AUTH_DAT kdata;
286 static des_cblock crypt_session_key;
287 char    krb_realm[REALM_SZ];
288 char    **save_argv(), *krb_realmofhost();
289 #ifndef HAVE_STRSAVE
290 static char *strsave();
291 #endif
292 #ifdef NOENCRYPTION
293 #define des_read        read
294 #define des_write       write
295 #else /* !NOENCRYPTION */
296 void    send_auth(), answer_auth();
297 int     encryptflag = 0;
298 #endif /* NOENCRYPTION */
299 #include "rpaths.h"
300 #else /* !KERBEROS */
301 #define des_read        read
302 #define des_write       write
303 #endif /* KERBEROS */
304
305 kstream krem;
306 int     errs;
307 #ifdef POSIX
308 void    lostconn();
309 #else
310 int     lostconn();
311 #endif
312 int     errno;
313 #ifndef __NetBSD__
314 extern char     *sys_errlist[];
315 #endif
316 int     iamremote, targetshouldbedirectory;
317 int     iamrecursive;
318 int     pflag;
319 int     force_net;
320 struct  passwd *pwd;
321 int     userid;
322 int     port;
323
324 char    *getenv();
325
326 struct buffer {
327         int     cnt;
328         char    *buf;
329 } *allocbuf();
330
331 #define NULLBUF (struct buffer *) 0
332
333 /*VARARGS*/
334 int     error();
335
336 #define ga()            (void) kstream_write (krem, "", 1)
337
338 main(argc, argv)
339         int argc;
340         char **argv;
341 {
342         char *targ, *host, *src;
343         char *suser, *tuser, *thost;
344         int i;
345         char buf[BUFSIZ], cmd[50 + REALM_SZ];
346         char portarg[20], rcpportarg[20];
347 #ifdef ATHENA
348         static char curhost[256];
349 #endif /* ATHENA */
350 #ifdef KERBEROS
351         char realmarg[REALM_SZ + 5];
352         long authopts;
353         char **orig_argv = save_argv(argc, argv);
354 #endif /* KERBEROS */
355
356         portarg[0] = '\0';
357         rcpportarg[0] = '\0';
358         realmarg[0] = '\0';
359
360         pwd = getpwuid(userid = getuid());
361         if (pwd == 0) {
362                 fprintf(stderr, "who are you?\n");
363                 exit(1);
364         }
365
366 #ifdef KERBEROS
367         krb_realm[0] = '\0';            /* Initially no kerberos realm set */
368 #endif /* KERBEROS */
369         for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
370                 (*argv)++;
371                 while (**argv) switch (*(*argv)++) {
372
373                     case 'r':
374                         iamrecursive++;
375                         break;
376
377                     case 'p':           /* preserve mtimes and atimes */
378                         pflag++;
379                         break;
380
381                     case 'P':           /* Set port to use.  */
382                         port = atoi(*argv);
383                         sprintf(portarg, " -p%d", port);
384                         sprintf(rcpportarg, " -P%d", port);
385                         port = htons(port);
386                         goto next_arg;
387
388                     case 'N':
389                         /* Force use of network even on local machine.  */
390                         force_net++;
391                         break;
392
393 #ifdef KERBEROS
394 #ifndef NOENCRYPTION
395                     case 'x':
396                         encryptflag++;
397                         break;
398 #endif
399                     case 'k':           /* Change kerberos realm */
400                         argc--, argv++;
401                         if (argc == 0) 
402                           usage();
403                         strncpy(krb_realm,*argv,REALM_SZ);
404                         sprintf(realmarg, " -k %s", krb_realm);
405                         goto next_arg;
406 #endif /* KERBEROS */
407                     /* The rest of these are not for users. */
408                     case 'd':
409                         targetshouldbedirectory = 1;
410                         break;
411
412                     case 'f':           /* "from" */
413                         iamremote = 1;
414 #if defined(KERBEROS) && !defined(NOENCRYPTION)
415                         if (encryptflag) {
416                                 answer_auth();
417                                 krem = kstream_create_rcp_from_fd (rem,
418                                                                    &schedule,
419                                                                    &crypt_session_key);
420                         } else
421                                 krem = kstream_create_from_fd (rem, 0, 0);
422                         kstream_set_buffer_mode (krem, 0);
423 #endif /* KERBEROS && !NOENCRYPTION */
424                         (void) response();
425                         (void) setuid(userid);
426                         source(--argc, ++argv);
427                         exit(errs);
428
429                     case 't':           /* "to" */
430                         iamremote = 1;
431 #if defined(KERBEROS) && !defined(NOENCRYPTION)
432                         if (encryptflag) {
433                                 answer_auth();
434                                 krem = kstream_create_rcp_from_fd (rem,
435                                                                    &schedule,
436                                                                    &crypt_session_key);
437                         } else
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);
443                         exit(errs);
444
445                     default:
446                         usage();
447                 }
448 #ifdef KERBEROS
449               next_arg: ;
450 #endif /* KERBEROS */
451         }
452         usage();
453 }
454
455 verifydir(cp)
456         char *cp;
457 {
458         struct stat stb;
459
460         if (stat(cp, &stb) >= 0) {
461                 if ((stb.st_mode & S_IFMT) == S_IFDIR)
462                         return;
463                 errno = ENOTDIR;
464         }
465         error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
466         exit(1);
467 }
468
469 source(argc, argv)
470         int argc;
471         char **argv;
472 {
473         char *last, *name;
474         struct stat stb;
475         static struct buffer buffer;
476         struct buffer *bp;
477         int x, readerr, f, amt;
478         off_t i;
479         char buf[BUFSIZ];
480
481         for (x = 0; x < argc; x++) {
482                 name = argv[x];
483                 if ((f = open(name, 0)) < 0) {
484                         error("rcp: %s: %s\n", name, sys_errlist[errno]);
485                         continue;
486                 }
487                 if (fstat(f, &stb) < 0)
488                         goto notreg;
489                 switch (stb.st_mode&S_IFMT) {
490
491                 case S_IFREG:
492                         break;
493
494                 case S_IFDIR:
495                         if (iamrecursive) {
496                                 (void) close(f);
497                                 rsource(name, &stb);
498                                 continue;
499                         }
500                         /* fall into ... */
501                 default:
502 notreg:
503                         (void) close(f);
504                         error("rcp: %s: not a plain file\n", name);
505                         continue;
506                 }
507                 last = strrchr(name, '/');
508                 if (last == 0)
509                         last = name;
510                 else
511                         last++;
512                 if (pflag) {
513                         /*
514                          * Make it compatible with possible future
515                          * versions expecting microseconds.
516                          */
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) {
521                                 (void) close(f);
522                                 continue;
523                         }
524                 }
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) {
529                         (void) close(f);
530                         continue;
531                 }
532                 if ((bp = allocbuf(&buffer, f, BUFSIZ)) == NULLBUF) {
533                         (void) close(f);
534                         continue;
535                 }
536                 readerr = 0;
537                 for (i = 0; i < stb.st_size; i += bp->cnt) {
538                         amt = 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)
542                                 readerr = errno;
543                         kstream_write (krem, bp->buf, amt);
544                 }
545                 (void) close(f);
546                 if (readerr == 0)
547                         ga();
548                 else
549                         error("rcp: %s: %s\n", name, sys_errlist[readerr]);
550                 (void) response();
551         }
552 }
553
554 #ifndef USE_DIRENT_H
555 #include <sys/dir.h>
556 #else
557 #include <dirent.h>
558 #endif
559
560 rsource(name, statp)
561         char *name;
562         struct stat *statp;
563 {
564         DIR *d = opendir(name);
565         char *last;
566         char buf[BUFSIZ];
567         char *bufv[1];
568 #ifdef USE_DIRENT_H
569         struct dirent *dp;
570 #else
571         struct direct *dp;
572 #endif
573
574         if (d == 0) {
575                 error("rcp: %s: %s\n", name, sys_errlist[errno]);
576                 return;
577         }
578         last = strrchr(name, '/');
579         if (last == 0)
580                 last = name;
581         else
582                 last++;
583         if (pflag) {
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) {
588                         closedir(d);
589                         return;
590                 }
591         }
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) {
595                 closedir(d);
596                 return;
597         }
598         while (dp = readdir(d)) {
599                 if (dp->d_ino == 0)
600                         continue;
601                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
602                         continue;
603                 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
604                         error("%s/%s: Name too long.\n", name, dp->d_name);
605                         continue;
606                 }
607                 (void) sprintf(buf, "%s/%s", name, dp->d_name);
608                 bufv[0] = buf;
609                 source(1, bufv);
610         }
611         closedir(d);
612         kstream_write (krem, "E\n", 2);
613         (void) response();
614 }
615
616 response()
617 {
618         char resp, c, rbuf[BUFSIZ], *cp = rbuf;
619
620         if (kstream_read (krem, &resp, 1) != 1)
621                 lostconn();
622         switch (resp) {
623
624         case 0:                         /* ok */
625                 return (0);
626
627         default:
628                 *cp++ = resp;
629                 /* fall into... */
630         case 1:                         /* error, followed by err msg */
631         case 2:                         /* fatal error, "" */
632                 do {
633                         if (kstream_read (krem, &c, 1) != 1)
634                                 lostconn();
635                         *cp++ = c;
636                 } while (cp < &rbuf[BUFSIZ] && c != '\n');
637                 if (iamremote == 0)
638                         (void) write(2, rbuf, cp - rbuf);
639                 errs++;
640                 if (resp == 1)
641                         return (-1);
642                 exit(1);
643         }
644         /*NOTREACHED*/
645 }
646
647 #ifdef POSIX
648 void
649 #else
650 int
651 #endif
652 lostconn()
653 {
654
655         if (iamremote == 0)
656                 fprintf(stderr, "rcp: lost connection\n");
657         exit(1);
658 }
659
660 #if !defined(HAS_UTIMES)
661 #include <utime.h>
662 #include <sys/time.h>
663
664 /*
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.
669  */
670 int utimes(file, tvp)
671 const char *file;
672 struct timeval *tvp;
673 {
674         struct utimbuf times;
675
676         times.actime = tvp[0].tv_sec;
677         times.modtime = tvp[1].tv_sec;
678         return(utime(file, &times));
679 }
680 #endif
681
682 sink(argc, argv)
683         int argc;
684         char **argv;
685 {
686         off_t i, j;
687         char *targ, *whopp, *cp;
688         int of, mode, wrerr, exists, first, count, amt;
689         off_t size;
690         struct buffer *bp;
691         static struct buffer buffer;
692         struct stat stb;
693         int targisdir = 0;
694         int mask = umask(0);
695         char *myargv[1];
696         char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
697         int setimes = 0;
698         struct timeval tv[2];
699 #define atime   tv[0]
700 #define mtime   tv[1]
701 #define SCREWUP(str)    { whopp = str; goto screwup; }
702
703         if (!pflag)
704                 (void) umask(mask);
705         if (argc != 1) {
706                 error("rcp: ambiguous target\n");
707                 exit(1);
708         }
709         targ = *argv;
710         if (targetshouldbedirectory)
711                 verifydir(targ);
712         ga();
713         if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
714                 targisdir = 1;
715         for (first = 1; ; first = 0) {
716                 cp = cmdbuf;
717                 if (kstream_read (krem, cp, 1) <= 0)
718                         return;
719                 if (*cp++ == '\n')
720                         SCREWUP("unexpected '\\n'");
721                 do {
722                         if (kstream_read(krem, cp, 1) != 1)
723                                 SCREWUP("lost connection");
724                 } while (*cp++ != '\n');
725                 *cp = 0;
726                 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
727                         if (iamremote == 0)
728                                 (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
729                         if (cmdbuf[0] == '\02')
730                                 exit(1);
731                         errs++;
732                         continue;
733                 }
734                 *--cp = 0;
735                 cp = cmdbuf;
736                 if (*cp == 'E') {
737                         ga();
738                         return;
739                 }
740
741 #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
742                 if (*cp == 'T') {
743                         setimes++;
744                         cp++;
745                         getnum(mtime.tv_sec);
746                         if (*cp++ != ' ')
747                                 SCREWUP("mtime.sec not delimited");
748                         getnum(mtime.tv_usec);
749                         if (*cp++ != ' ')
750                                 SCREWUP("mtime.usec not delimited");
751                         getnum(atime.tv_sec);
752                         if (*cp++ != ' ')
753                                 SCREWUP("atime.sec not delimited");
754                         getnum(atime.tv_usec);
755                         if (*cp++ != '\0')
756                                 SCREWUP("atime.usec not delimited");
757                         ga();
758                         continue;
759                 }
760                 if (*cp != 'C' && *cp != 'D') {
761                         /*
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
766                          * followed.
767                          */
768                         if (first) {
769                                 error("%s\n", cp);
770                                 exit(1);
771                         }
772                         SCREWUP("expected control record");
773                 }
774                 cp++;
775                 mode = 0;
776                 for (; cp < cmdbuf+5; cp++) {
777                         if (*cp < '0' || *cp > '7')
778                                 SCREWUP("bad mode");
779                         mode = (mode << 3) | (*cp - '0');
780                 }
781                 if (*cp++ != ' ')
782                         SCREWUP("mode not delimited");
783                 size = 0;
784                 while (isdigit(*cp))
785                         size = size * 10 + (*cp++ - '0');
786                 if (*cp++ != ' ')
787                         SCREWUP("size not delimited");
788                 if (targisdir)
789                         (void) sprintf(nambuf, "%s%s%s", targ,
790                             *targ ? "/" : "", cp);
791                 else
792                         (void) strcpy(nambuf, targ);
793                 exists = stat(nambuf, &stb) == 0;
794                 if (cmdbuf[0] == 'D') {
795                         if (exists) {
796                                 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
797                                         errno = ENOTDIR;
798                                         goto bad;
799                                 }
800                                 if (pflag)
801                                         (void) chmod(nambuf, mode);
802                         } else if (mkdir(nambuf, mode) < 0)
803                                 goto bad;
804                         myargv[0] = nambuf;
805                         sink(1, myargv);
806                         if (setimes) {
807                                 setimes = 0;
808                                 if (utimes(nambuf, tv) < 0)
809                                         error("rcp: can't set times on %s: %s\n",
810                                             nambuf, sys_errlist[errno]);
811                         }
812                         continue;
813                 }
814                 if ((of = open(nambuf, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) {
815         bad:
816                         error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
817                         continue;
818                 }
819 #ifdef NO_FCHMOD
820                 if (exists && pflag)
821                         (void) chmod(nambuf, mode);
822 #else
823                 if (exists && pflag)
824                         (void) fchmod(of, mode);
825 #endif
826                 ga();
827                 if ((bp = allocbuf(&buffer, of, BUFSIZ)) == NULLBUF) {
828                         (void) close(of);
829                         continue;
830                 }
831                 cp = bp->buf;
832                 count = 0;
833                 wrerr = 0;
834                 for (i = 0; i < size; i += BUFSIZ) {
835                         amt = BUFSIZ;
836                         if (i + amt > size)
837                                 amt = size - i;
838                         count += amt;
839                         do {
840                                 j = kstream_read(krem, cp, amt);
841                                 if (j <= 0) {
842                                         if (j == 0)
843                                             error("rcp: dropped connection");
844                                         else
845                                             error("rcp: %s\n",
846                                                 sys_errlist[errno]);
847                                         exit(1);
848                                 }
849                                 amt -= j;
850                                 cp += j;
851                         } while (amt > 0);
852                         if (count == bp->cnt) {
853                                 if (wrerr == 0 &&
854                                     write(of, bp->buf, count) != count)
855                                         wrerr++;
856                                 count = 0;
857                                 cp = bp->buf;
858                         }
859                 }
860                 if (count != 0 && wrerr == 0 &&
861                     write(of, bp->buf, count) != count)
862                         wrerr++;
863 #ifndef __SCO__
864                 if (ftruncate(of, size))
865                         error("rcp: can't truncate %s: %s\n",
866                             nambuf, sys_errlist[errno]);
867 #endif
868                 (void) close(of);
869                 (void) response();
870                 if (setimes) {
871                         setimes = 0;
872                         if (utimes(nambuf, tv) < 0)
873                                 error("rcp: can't set times on %s: %s\n",
874                                     nambuf, sys_errlist[errno]);
875                 }                                  
876                 if (wrerr)
877                         error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
878                 else
879                         ga();
880         }
881 screwup:
882         error("rcp: protocol screwup: %s\n", whopp);
883         exit(1);
884 }
885
886 struct buffer *
887 allocbuf(bp, fd, blksize)
888         struct buffer *bp;
889         int fd, blksize;
890 {
891         int size;
892 #ifndef NOSTBLKSIZE
893         struct stat stb;
894
895         if (fstat(fd, &stb) < 0) {
896                 error("rcp: fstat: %s\n", sys_errlist[errno]);
897                 return (NULLBUF);
898         }
899         size = roundup(stb.st_blksize, blksize);
900         if (size == 0)
901 #endif
902                 size = blksize;
903         if (bp->cnt < size) {
904                 if (bp->buf != 0)
905                         free(bp->buf);
906                 bp->buf = (char *)malloc((unsigned) size);
907                 if (bp->buf == 0) {
908                         error("rcp: malloc: out of memory\n");
909                         return (NULLBUF);
910                 }
911         }
912         bp->cnt = size;
913         return (bp);
914 }
915
916 /*VARARGS1*/
917 error(fmt, a1, a2, a3, a4, a5)
918         char *fmt;
919         int a1, a2, a3, a4, a5;
920 {
921         char buf[BUFSIZ], *cp = buf;
922
923         errs++;
924         *cp++ = 1;
925         (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
926         if (krem)
927           (void) kstream_write(krem, buf, strlen(buf));
928         if (iamremote == 0)
929                 (void) write(2, buf+1, strlen(buf+1));
930 }
931
932 usage()
933 {
934   fprintf(stderr,
935 "v4rcp: this program only acts as a server, and is not for user function.\n");
936   exit(1);
937 }
938
939 #ifdef KERBEROS
940
941 char **
942 save_argv(argc, argv)
943 int argc;
944 char **argv;
945 {
946         register int i;
947
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]);
954         return(local_argv);
955 }
956
957 #ifndef HAVE_STRSAVE
958 static char *
959 strsave(sp)
960 char *sp;
961 {
962         register char *ret;
963         
964         if((ret = (char *)malloc((unsigned) strlen(sp)+1)) == NULL) {
965                 fprintf(stderr, "rcp: no memory for saving args\n");
966                 exit(1);
967         }
968         (void) strcpy(ret,sp);
969         return(ret);
970 }
971 #endif
972
973 #ifndef NOENCRYPTION
974 #undef rem
975 #define rem 0
976
977 void
978 answer_auth()
979 {
980         int sin_len, status;
981         long authopts = KOPT_DO_MUTUAL;
982         char instance[INST_SZ];
983         char version[9];
984         char *srvtab;
985         char *envaddr;
986
987 #if 0
988         sin_len = sizeof (struct sockaddr_in);
989         if (getpeername(rem, &foreign, &sin_len) < 0) {
990                 perror("getpeername");
991                 exit(1);
992         }
993
994         sin_len = sizeof (struct sockaddr_in);
995         if (getsockname(rem, &local, &sin_len) < 0) {
996                 perror("getsockname");
997                 exit(1);
998         }
999 #else
1000         if (envaddr = getenv("KRB5LOCALADDR")) {
1001 #ifdef HAVE_INET_ATON
1002           inet_aton(envaddr,  &local.sin_addr);
1003 #else
1004           local.sin_addr.s_addr = inet_addr(envaddr);
1005 #endif
1006           local.sin_family = AF_INET;
1007           local.sin_port = 0;
1008         } else {
1009           fprintf(stderr, "v4rcp: couldn't get local address (KRB5LOCALADDR)\n");
1010           exit(1);
1011         }
1012         if (envaddr = getenv("KRB5REMOTEADDR")) {
1013 #ifdef HAVE_INET_ATON
1014           inet_aton(envaddr,  &foreign.sin_addr);
1015 #else
1016           foreign.sin_addr.s_addr = inet_addr(envaddr);
1017 #endif
1018           foreign.sin_family = AF_INET;
1019           foreign.sin_port = 0;
1020         } else {
1021           fprintf(stderr, "v4rcp: couldn't get remote address (KRB5REMOTEADDR)\n");
1022           exit(1);
1023         }
1024
1025 #endif
1026         strcpy(instance, "*");
1027
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");
1038         if (srvtab == NULL)
1039                 srvtab = "";
1040         if (*srvtab != '\0')
1041                 (void) setuid (userid);
1042
1043         if ((status = krb_recvauth(authopts, rem, &ticket, "rcmd", instance,
1044                                    &foreign,
1045                                    &local,
1046                                    &kdata,
1047                                    srvtab,
1048                                    schedule,
1049                                    version)) != KSUCCESS) {
1050                 fprintf(stderr, "krb_recvauth mutual fail: %s\n",
1051                         krb_get_err_text(status));
1052                 exit(1);
1053         }
1054         memcpy(&crypt_session_key, &kdata.session, sizeof (crypt_session_key));
1055         return;
1056 }
1057 #endif /* !NOENCRYPTION */
1058
1059 #endif /* KERBEROS */