This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / appl / bsd / krcp.c
1 /*
2  *      appl/bsd/krcp.c
3  */
4
5 /*
6  * Copyright (c) 1983 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms are permitted
10  * provided that the above copyright notice and this paragraph are
11  * duplicated in all such forms and that any documentation,
12  * advertising materials, and other materials related to such
13  * distribution and use acknowledge that the software was developed
14  * by the University of California, Berkeley.  The name of the
15  * University may not be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
19  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21
22 #ifndef lint
23 char copyright[] =
24   "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
25  All rights reserved.\n";
26 #endif /* not lint */
27
28 /* based on @(#)rcp.c   5.10 (Berkeley) 9/20/88 */
29
30      /*
31       * rcp
32       */
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #include <sys/param.h>
41 #ifndef _TYPES_
42 #include <sys/types.h>
43 #define _TYPES_
44 #endif
45 #include <sys/file.h>
46 #include <fcntl.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <sys/ioctl.h>
50      
51 #include <netinet/in.h>
52      
53 #include <stdio.h>
54 #include <signal.h>
55 #include <pwd.h>
56 #include <ctype.h>
57 #include <netdb.h>
58 #include <errno.h>
59 #include <string.h>
60 #ifdef HAVE_VFORK_H
61 #include <vfork.h>
62 #endif
63 #ifdef HAVE_STDARG_H
64 #include <stdarg.h>
65 #else
66 #include <varargs.h>
67 #endif
68 #include <sys/wait.h>
69
70 #ifdef KERBEROS
71 #include <krb5.h>
72 #include <k5-util.h>
73 #include <com_err.h>
74
75 #ifdef KRB5_KRB4_COMPAT
76 #include <kerberosIV/krb.h>
77 #endif
78
79 #include "defines.h"
80
81 #define RCP_BUFSIZ 4096
82      
83 int sock;
84 struct sockaddr_in local, foreign; /* set up by kcmd used by v4_send_auth */
85 char *krb_realm = NULL;
86 char *krb_cache = NULL;
87 char *krb_config = NULL;
88 krb5_encrypt_block eblock;         /* eblock for encrypt/decrypt */
89 krb5_context bsd_context;
90
91 #ifdef KRB5_KRB4_COMPAT
92 Key_schedule v4_schedule;
93 CREDENTIALS v4_cred;
94 KTEXT_ST v4_ticket;
95 MSG_DAT v4_msg_data;
96 #endif
97
98 void    v4_send_auth(char *, char *), try_normal(char **);
99 char    **save_argv(int, char **);
100 #ifndef HAVE_STRSAVE
101 char    *strsave();
102 #endif
103 int     rcmd_stream_write(), rcmd_stream_read();
104 void    usage(void), sink(int, char **),
105     source(int, char **), rsource(char *, struct stat *), verifydir(char *), 
106     answer_auth(char *, char *);
107 int     response(void), hosteq(char *, char *), okname(char *), 
108     susystem(char *);
109 int     encryptflag = 0;
110
111 #ifndef UCB_RCP
112 #define UCB_RCP "/bin/rcp"
113 #endif
114
115 #endif /* KERBEROS */
116
117 int     rem;
118 char    *colon(char *);
119 int     errs;
120 krb5_sigtype    lostconn(int);
121 int     iamremote, targetshouldbedirectory;
122 int     iamrecursive;
123 int     pflag;
124 int     forcenet;
125 struct  passwd *pwd;
126 int     userid;
127 int     port = 0;
128
129 struct buffer {
130     unsigned int cnt;
131     char        *buf;
132 };
133
134 struct buffer *allocbuf(struct buffer *, int, int);
135
136 #define NULLBUF (struct buffer *) 0
137   
138 void    error (char *fmt, ...)
139 #if !defined (__cplusplus) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
140        __attribute__ ((__format__ (__printf__, 1, 2)))
141 #endif
142      ;
143
144 #define ga()            (void) rcmd_stream_write(rem, "", 1, 0)
145
146 int main(argc, argv)
147      int argc;
148      char **argv;
149 {
150     char *targ, *host, *src;
151     char *suser, *tuser, *thost;
152     int i;
153     unsigned int cmdsiz = 30;
154     char buf[RCP_BUFSIZ], cmdbuf[30];
155     char *cmd = cmdbuf;
156     struct servent *sp;
157     static char curhost[256];
158 #ifdef POSIX_SIGNALS
159     struct sigaction sa;
160 #endif
161 #ifdef KERBEROS
162     krb5_flags authopts;
163     krb5_error_code status;     
164     int euid;
165     char **orig_argv = save_argv(argc, argv);
166     krb5_auth_context auth_context;
167     enum kcmd_proto kcmd_proto = KCMD_PROTOCOL_COMPAT_HACK;
168
169     status = krb5_init_context(&bsd_context);
170     if (status) {
171             com_err(argv[0], status, "while initializing krb5");
172             exit(1);
173     }
174 #endif
175
176     pwd = getpwuid(userid = getuid());
177     if (pwd == 0) {
178         fprintf(stderr, "who are you?\n");
179         exit(1);
180     }
181     
182     for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
183         (*argv)++;
184         while (**argv) switch (*(*argv)++) {
185             
186           case 'r':
187             iamrecursive++;
188             break;
189             
190           case 'p':             /* preserve mtimes and atimes */
191             pflag++;
192             break;
193             
194           case 'D':
195             argc--, argv++;
196             if (argc == 0)
197               usage();
198             port = htons(atoi(*argv));
199             goto next_arg;
200
201           case 'N':
202             forcenet++;
203             break;
204
205 #ifdef KERBEROS
206           case 'x':
207             encryptflag++;
208             break;
209           case 'k':             /* Change kerberos realm */
210             argc--, argv++;
211             if (argc == 0) 
212               usage();
213             if(!(krb_realm = (char *)malloc(strlen(*argv) + 1))){
214                 fprintf(stderr, "rcp: Cannot malloc.\n");
215                 exit(1);
216             }
217             strcpy(krb_realm, *argv);   
218             goto next_arg;
219           case 'c':             /* Change default ccache file */
220             argc--, argv++;
221             if (argc == 0) 
222               usage();
223             if(!(krb_cache = (char *)malloc(strlen(*argv) + 1))){
224                 fprintf(stderr, "rcp: Cannot malloc.\n");
225                 exit(1);
226             }
227             strcpy(krb_cache, *argv);   
228             goto next_arg;
229           case 'C':             /* Change default config file */
230             argc--, argv++;
231             if (argc == 0) 
232               usage();
233             if(!(krb_config = (char *)malloc(strlen(*argv) + 1))){
234                 fprintf(stderr, "rcp: Cannot malloc.\n");
235                 exit(1);
236             }
237             strcpy(krb_config, *argv);  
238             goto next_arg;
239           case 'P':
240             if (!strcmp (*argv, "O"))
241                 kcmd_proto = KCMD_OLD_PROTOCOL;
242             else if (!strcmp (*argv, "N"))
243                 kcmd_proto = KCMD_NEW_PROTOCOL;
244             else
245                 usage ();
246             goto next_arg;
247 #endif /* KERBEROS */
248             /* The rest of these are not for users. */
249           case 'd':
250             targetshouldbedirectory = 1;
251             break;
252             
253           case 'f':             /* "from" */
254             iamremote = 1;
255             rcmd_stream_init_normal();
256 #if defined(KERBEROS)
257             if (encryptflag)
258               answer_auth(krb_config, krb_cache);
259 #endif /* KERBEROS */
260
261             (void) response();
262             source(--argc, ++argv);
263             exit(errs);
264             
265           case 't':             /* "to" */
266             iamremote = 1;
267             rcmd_stream_init_normal();
268 #if defined(KERBEROS)
269             if (encryptflag)
270               answer_auth(krb_config, krb_cache);
271 #endif /* KERBEROS */
272
273             sink(--argc, ++argv);
274             exit(errs);
275             
276           default:
277             usage();
278         }
279       next_arg: ;
280     }
281     
282     if (argc < 2)
283       usage();
284     if (argc > 2)
285       targetshouldbedirectory = 1;
286     rem = -1;
287
288
289     if (port == 0) {
290 #ifdef KERBEROS
291       sp = getservbyname("kshell", "tcp");
292 #else
293       sp = getservbyname("shell", "tcp");
294 #endif /* KERBEROS */
295     
296       if (sp == NULL) {
297 #ifdef KERBEROS
298         fprintf(stderr, "rcp: kshell/tcp: unknown service\n");
299         try_normal(orig_argv);
300 #else
301         fprintf(stderr, "rcp: shell/tcp: unknown service\n");
302         exit(1);
303 #endif /* KERBEROS */
304       }
305       port = sp->s_port;
306     }
307
308 #ifdef KERBEROS
309     if (krb_realm != NULL)
310         cmdsiz += strlen(krb_realm);
311     if (krb_cache != NULL)
312         cmdsiz += strlen(krb_cache);
313     if (krb_config != NULL)
314         cmdsiz += strlen(krb_config);
315
316     if ((cmd = (char *)malloc(cmdsiz)) == NULL) {
317         fprintf(stderr, "rcp: Cannot malloc.\n");
318         exit(1);
319     }
320     (void) sprintf(cmd, "%srcp %s%s%s%s%s%s%s%s%s",
321                    encryptflag ? "-x " : "",
322
323                    iamrecursive ? " -r" : "", pflag ? " -p" : "", 
324                    targetshouldbedirectory ? " -d" : "",
325                    krb_realm != NULL ? " -k " : "",
326                    krb_realm != NULL ? krb_realm : "",
327                    krb_cache != NULL ? " -c " : "",
328                    krb_cache != NULL ? krb_cache : "",
329                    krb_config != NULL ? " -C " : "",
330                    krb_config != NULL ? krb_config : "");
331
332 #else /* !KERBEROS */
333     (void) sprintf(cmd, "rcp%s%s%s",
334                    iamrecursive ? " -r" : "", pflag ? " -p" : "", 
335                    targetshouldbedirectory ? " -d" : "");
336 #endif /* KERBEROS */
337     
338 #ifdef POSIX_SIGNALS
339     (void) sigemptyset(&sa.sa_mask);
340     sa.sa_flags = 0;
341     sa.sa_handler = lostconn;
342     (void) sigaction(SIGPIPE, &sa, (struct sigaction *)0);
343 #else
344     (void) signal(SIGPIPE, lostconn);
345 #endif
346     targ = colon(argv[argc - 1]);
347     
348     /* Check if target machine is the current machine. */
349     
350     gethostname(curhost, sizeof(curhost));
351     if (targ) {                         /* ... to remote */
352         *targ++ = 0;
353         if (hosteq(argv[argc - 1], curhost)) {
354             
355             /* If so, pretend there wasn't even one given
356              * check for an argument of just "host:", it
357              * should become "."
358              */
359             
360             if (*targ == 0) {
361                 targ = ".";
362                 argv[argc - 1] = targ;
363             }
364             else
365               argv[argc - 1] = targ;
366             targ = 0;
367         }
368     }
369     if (targ) {
370         /* Target machine is some remote machine */
371         if (*targ == 0)
372           targ = ".";
373         thost = strchr(argv[argc - 1], '@');
374         if (thost) {
375             *thost++ = 0;
376             tuser = argv[argc - 1];
377             if (*tuser == '\0')
378               tuser = NULL;
379             else if (!okname(tuser))
380               exit(1);
381         } else {
382             thost = argv[argc - 1];
383             tuser = NULL;
384         }
385         for (i = 0; i < argc - 1; i++) {
386             src = colon(argv[i]);
387             if (src) {          /* remote to remote */
388                 *src++ = 0;
389                 if (*src == 0)
390                   src = ".";
391                 host = strchr(argv[i], '@');
392                 if (host) {
393                     *host++ = 0;
394                     suser = argv[i];
395                     if (*suser == '\0')
396                       suser = pwd->pw_name;
397                     else if (!okname(suser))
398                       continue;
399                     (void) sprintf(buf,
400 #if defined(hpux) || defined(__hpux)
401                                    "remsh %s -l %s -n %s %s '%s%s%s:%s'",
402 #else
403                                    "rsh %s -l %s -n %s %s '%s%s%s:%s'",
404 #endif
405                                    host, suser, cmd, src,
406                                    tuser ? tuser : "",
407                                    tuser ? "@" : "",
408                                    thost, targ);
409                } else
410                    (void) sprintf(buf,
411 #if defined(hpux) || defined(__hpux)
412                                   "remsh %s -n %s %s '%s%s%s:%s'",
413 #else
414                                   "rsh %s -n %s %s '%s%s%s:%s'",
415 #endif
416                                    argv[i], cmd, src,
417                                    tuser ? tuser : "",
418                                    tuser ? "@" : "",
419                                    thost, targ);
420                 (void) susystem(buf);
421             } else {            /* local to remote */
422                 krb5_creds *cred;
423                 if (rem == -1) {
424                     (void) sprintf(buf, "%s -t %s",
425                                    cmd, targ);
426                     host = thost;
427 #ifdef KERBEROS
428                     authopts = AP_OPTS_MUTUAL_REQUIRED;
429                     status = kcmd(&sock, &host,
430                                   port,
431                                   pwd->pw_name,
432                                   tuser ? tuser :
433                                   pwd->pw_name,
434                                   buf,
435                                   0,
436                                   "host",
437                                   krb_realm,
438                                   &cred,  
439                                   0,  /* No seq # */
440                                   0,  /* No server seq # */
441                                   &local,
442                                   &foreign,
443                                   &auth_context, authopts,
444                                   0, /* Not any port # */
445                                   0,
446                                   &kcmd_proto);
447                     if (status) {
448                         if (kcmd_proto == KCMD_NEW_PROTOCOL)
449                             /* Don't fall back to less safe methods.  */
450                             exit (1);
451 #ifdef KRB5_KRB4_COMPAT
452                         fprintf(stderr, "Trying krb4 rcp...\n");
453                         if (strncmp(buf, "-x rcp", 6) == 0)
454                             memcpy(buf, "rcp -x", 6);
455                         status = k4cmd(&sock, &host, port,
456                                        pwd->pw_name,
457                                        tuser ? tuser : pwd->pw_name, buf,
458                                        0, &v4_ticket, "rcmd", krb_realm,
459                                        NULL, NULL, NULL,
460                                        &local, &foreign, 0L, 0);
461                         if (status)
462                             try_normal(orig_argv);
463                         if (encryptflag)
464                             v4_send_auth(host, krb_realm);
465                         rcmd_stream_init_krb4(v4_cred.session, encryptflag, 0,
466                                               0);
467 #else
468                         try_normal(orig_argv);
469 #endif
470                     }
471                     else {
472                         krb5_boolean similar;
473                         krb5_keyblock *key = &cred->keyblock;
474
475                         status = krb5_c_enctype_compare(bsd_context,
476                                                         ENCTYPE_DES_CBC_CRC,
477                                                         cred->keyblock.enctype,
478                                                         &similar);
479                         if (status)
480                             try_normal(orig_argv); /* doesn't return */
481
482                         if (!similar) {
483                             status = krb5_auth_con_getsendsubkey (bsd_context,
484                                                                   auth_context,
485                                                                   &key);
486                             if ((status || !key) && encryptflag)
487                                 try_normal(orig_argv);
488                         }
489                         if (key == 0)
490                             key = &cred->keyblock;
491
492                         rcmd_stream_init_krb5(key, encryptflag, 0, 1,
493                                               kcmd_proto);
494                     }
495                     rem = sock;
496 #else
497                     rem = rcmd(&host, port, pwd->pw_name,
498                                tuser ? tuser : pwd->pw_name,
499                                buf, 0);
500                     if (rem < 0)
501                       exit(1);
502 #endif /* KERBEROS */
503                     if (response() < 0)
504                       exit(1);
505                 }
506                 source(1, argv+i);
507             }
508         }
509     } else {                            /* ... to local */
510         if (targetshouldbedirectory)
511           verifydir(argv[argc - 1]);
512         for (i = 0; i < argc - 1; i++) {
513             src = colon(argv[i]);
514             /* Check if source machine is current machine */
515             if (src) {
516                 *src++ = 0;
517                 if (hosteq(argv[i], curhost)) {
518                     
519                     /* If so, pretend src machine never given */
520                     
521                     if (*src == 0) {
522                         error("rcp: no path given in arg: %s:\n",
523                               argv[i]);
524                         errs++;
525                         continue;
526                     }
527                     argv[i] = src;
528                     src = 0;
529                 } else {
530                     /* not equiv, return colon */
531                     *(--src) = ':';
532                 }
533             }
534             if (src == 0) {             /* local to local */
535                 (void) sprintf(buf, "/bin/cp%s%s %s %s",
536                                iamrecursive ? " -r" : "",
537                                pflag ? " -p" : "",
538                                argv[i], argv[argc - 1]);
539                 (void) susystem(buf);
540             } else {            /* remote to local */
541                 krb5_creds *cred;
542                 *src++ = 0;
543                 if (*src == 0)
544                   src = ".";
545                 host = strchr(argv[i], '@');
546                 if (host) {
547                     *host++ = 0;
548                     suser = argv[i];
549                     if (*suser == '\0')
550                       suser = pwd->pw_name;
551                     else if (!okname(suser))
552                       continue;
553                 } else {
554                     host = argv[i];
555                     suser = pwd->pw_name;
556                 }
557                 (void) sprintf(buf, "%s -f %s", cmd, src);
558 #ifdef KERBEROS
559                 authopts = AP_OPTS_MUTUAL_REQUIRED;
560                 status = kcmd(&sock, &host,
561                               port,
562                               pwd->pw_name,  suser,
563                               buf,
564                               0,
565                               "host",
566                               krb_realm,
567                               &cred,  
568                               0,  /* No seq # */
569                               0,  /* No server seq # */
570                               (struct sockaddr_in *) 0,
571                               &foreign,
572                               &auth_context, authopts,
573                               0, /* Not any port # */
574                               0,
575                               &kcmd_proto);
576                 if (status) {
577                         if (kcmd_proto == KCMD_NEW_PROTOCOL)
578                             /* Don't fall back to less safe methods.  */
579                             exit (1);
580 #ifdef KRB5_KRB4_COMPAT
581                         fprintf(stderr, "Trying krb4 rcp...\n");
582                         if (strncmp(buf, "-x rcp", 6) == 0)
583                             memcpy(buf, "rcp -x", 6);
584                         status = k4cmd(&sock, &host, port,
585                                        pwd->pw_name, suser, buf,
586                                        0, &v4_ticket, "rcmd", krb_realm,
587                                        NULL, NULL, NULL,
588                                        &local, &foreign, 0L, 0);
589                         if (status)
590                             try_normal(orig_argv);
591                         if (encryptflag)
592                             v4_send_auth(host, krb_realm);
593                         rcmd_stream_init_krb4(v4_cred.session, encryptflag, 0,
594                                               0);
595 #else
596                         try_normal(orig_argv);
597 #endif
598                 } else {
599                     krb5_keyblock *key = &cred->keyblock;
600
601                     if (kcmd_proto == KCMD_NEW_PROTOCOL) {
602                         status = krb5_auth_con_getsendsubkey (bsd_context,
603                                                               auth_context,
604                                                               &key);
605                         if (status) {
606                             com_err (argv[0], status,
607                                      "determining subkey for session");
608                             exit (1);
609                         }
610                         if (!key) {
611                             com_err (argv[0], 0,
612                                      "no subkey negotiated for connection");
613                             exit (1);
614                         }
615                     }
616
617                     rcmd_stream_init_krb5(key, encryptflag, 0, 1, kcmd_proto);
618                 }
619                 rem = sock; 
620                                    
621                 euid = geteuid();
622                 if (euid == 0) {
623                     (void) setuid(0);
624                     if(krb5_seteuid(userid)) {
625                         perror("rcp seteuid user"); errs++; exit(errs);
626                     }
627                 }
628                 sink(1, argv+argc-1);
629                 if (euid == 0) {
630                     if(krb5_seteuid(0)) {
631                         perror("rcp seteuid 0"); errs++; exit(errs);
632                     }
633                 }
634 #else
635                 rem = rcmd(&host, port, pwd->pw_name, suser,
636                            buf, 0);
637                 if (rem < 0)
638                   continue;
639                 rcmd_stream_init_normal();
640 #ifdef HAVE_SETREUID
641                 (void) setreuid(0, userid);
642                 sink(1, argv+argc-1);
643                 (void) setreuid(userid, 0);
644 #else
645                 (void) setuid(0);
646                 if(seteuid(userid)) {
647                   perror("rcp seteuid user"); errs++; exit(errs);
648                 }
649                 sink(1, argv+argc-1);
650                 if(seteuid(0)) {
651                   perror("rcp seteuid 0"); errs++; exit(errs);
652                 }
653 #endif
654 #endif /* KERBEROS */
655                 (void) close(rem);
656                 rem = -1;
657             }
658         }
659     }
660     exit(errs);
661 }
662
663
664
665 void verifydir(cp)
666      char *cp;
667 {
668     struct stat stb;
669     
670     if (stat(cp, &stb) >= 0) {
671         if ((stb.st_mode & S_IFMT) == S_IFDIR)
672           return;
673         errno = ENOTDIR;
674     }
675     error("rcp: %s: %s.\n", cp, error_message(errno));
676     exit(1);
677 }
678
679
680
681 char *colon(cp)
682      char *cp;
683 {
684     
685     while (*cp) {
686         if (*cp == ':')
687           return (cp);
688         if (*cp == '/')
689           return (0);
690         cp++;
691     }
692     return (0);
693 }
694
695
696
697 int okname(cp0)
698      char *cp0;
699 {
700     register char *cp = cp0;
701     register int c;
702     
703     do {
704         c = *cp;
705         if (c & 0200)
706           goto bad;
707         if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
708           goto bad;
709         cp++;
710     } while (*cp);
711     return (1);
712   bad:
713     fprintf(stderr, "rcp: invalid user name %s\n", cp0);
714     return (0);
715 }
716
717
718
719 int susystem(s)
720      char *s;
721 {
722     int status;
723     pid_t pid, w;
724 #ifdef POSIX_SIGNALS
725     struct sigaction sa, isa, qsa;
726 #else
727     register krb5_sigtype (bsd_context, *istat)(), (*qstat)();
728 #endif
729     
730     if ((pid = vfork()) == 0) {
731         execl("/bin/sh", "sh", "-c", s, (char *)0);
732         _exit(127);
733     }
734
735 #ifdef POSIX_SIGNALS
736     (void) sigemptyset(&sa.sa_mask);
737     sa.sa_flags = 0;
738     sa.sa_handler = SIG_IGN;
739     (void) sigaction(SIGINT, &sa, &isa);
740     (void) sigaction(SIGQUIT, &sa, &qsa);
741 #else
742     istat = signal(SIGINT, SIG_IGN);
743     qstat = signal(SIGQUIT, SIG_IGN);
744 #endif
745     
746 #ifdef HAVE_WAITPID
747     w = waitpid(pid, &status, 0);
748 #else
749     while ((w = wait(&status)) != pid && w != -1) /*void*/ ;
750 #endif
751     if (w == (pid_t)-1)
752       status = -1;
753
754 #ifdef POSIX_SIGNALS
755     (void) sigaction(SIGINT, &isa, (struct sigaction *)0);
756     (void) sigaction(SIGQUIT, &qsa, (struct sigaction *)0);
757 #else    
758     (void) signal(SIGINT, istat);
759     (void) signal(SIGQUIT, qstat);
760 #endif
761     
762     return (status);
763 }
764
765 void source(argc, argv)
766      int argc;
767      char **argv;
768 {
769     char *last, *name;
770     struct stat stb;
771     static struct buffer buffer;
772     struct buffer *bp;
773     int x, readerr, f;
774     unsigned int amt;
775     off_t i;
776     char buf[RCP_BUFSIZ];
777     
778     for (x = 0; x < argc; x++) {
779         name = argv[x];
780         if ((f = open(name, 0)) < 0) {
781             error("rcp: %s: %s\n", name, error_message(errno));
782             continue;
783         }
784         if (fstat(f, &stb) < 0)
785           goto notreg;
786         switch (stb.st_mode&S_IFMT) {
787             
788           case S_IFREG:
789             break;
790             
791           case S_IFDIR:
792             if (iamrecursive) {
793                 (void) close(f);
794                 rsource(name, &stb);
795                 continue;
796             }
797             /* fall into ... */
798           default:
799           notreg:
800             (void) close(f);
801             error("rcp: %s: not a plain file\n", name);
802             continue;
803         }
804         last = strrchr(name, '/');
805         if (last == 0)
806           last = name;
807         else
808           last++;
809         if (pflag) {
810             /*
811              * Make it compatible with possible future
812              * versions expecting microseconds.
813              */
814             (void) sprintf(buf, "T%ld 0 %ld 0\n",
815                            stb.st_mtime, stb.st_atime);
816             (void) rcmd_stream_write(rem, buf, strlen(buf), 0);
817             if (response() < 0) {
818                 (void) close(f);
819                 continue;
820             }
821         }
822         (void) sprintf(buf, "C%04o %ld %s\n",
823                        (int) stb.st_mode&07777, (long ) stb.st_size, last);
824         (void) rcmd_stream_write(rem, buf, strlen(buf), 0);
825         if (response() < 0) {
826             (void) close(f);
827             continue;
828         }
829         if ((bp = allocbuf(&buffer, f, RCP_BUFSIZ)) == NULLBUF) {
830             (void) close(f);
831             continue;
832         }
833         readerr = 0;
834         for (i = 0; i < stb.st_size; i += bp->cnt) {
835             amt = bp->cnt;
836             if (i + amt > stb.st_size)
837               amt = stb.st_size - i;
838             if (readerr == 0 && read(f, bp->buf, amt) != amt)
839               readerr = errno;
840             (void) rcmd_stream_write(rem, bp->buf, amt, 0);
841         }
842         (void) close(f);
843         if (readerr == 0)
844           ga();
845         else
846           error("rcp: %s: %s\n", name, error_message(readerr));
847         (void) response();
848     }
849 }
850
851
852
853 #ifndef USE_DIRENT_H
854 #include <sys/dir.h>
855 #else
856 #include <dirent.h>
857 #endif
858
859 void rsource(name, statp)
860      char *name;
861      struct stat *statp;
862 {
863     DIR *d = opendir(name);
864     char *last;
865 #ifdef USE_DIRENT_H
866     struct dirent *dp;
867 #else
868     struct direct *dp;
869 #endif
870     char buf[RCP_BUFSIZ];
871     char *bufv[1];
872     
873     if (d == 0) {
874         error("rcp: %s: %s\n", name, error_message(errno));
875         return;
876     }
877     last = strrchr(name, '/');
878     if (last == 0)
879       last = name;
880     else
881       last++;
882     if (pflag) {
883         (void) sprintf(buf, "T%ld 0 %ld 0\n",
884                        statp->st_mtime, statp->st_atime);
885         (void) rcmd_stream_write(rem, buf, strlen(buf), 0);
886         if (response() < 0) {
887             closedir(d);
888             return;
889         }
890     }
891     (void) sprintf(buf, "D%04lo %d %s\n", (long) statp->st_mode&07777, 0, 
892                    last);
893     (void) rcmd_stream_write(rem, buf, strlen(buf), 0);
894     if (response() < 0) {
895         closedir(d);
896         return;
897     }
898     while ((dp = readdir(d)) != NULL) {
899         if (dp->d_ino == 0)
900           continue;
901         if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
902           continue;
903         if (strlen(name) + 1 + strlen(dp->d_name) >= RCP_BUFSIZ - 1) {
904             error("%s/%s: Name too long.\n", name, dp->d_name);
905             continue;
906         }
907         (void) sprintf(buf, "%s/%s", name, dp->d_name);
908         bufv[0] = buf;
909         source(1, bufv);
910     }
911     closedir(d);
912     (void) rcmd_stream_write(rem, "E\n", 2, 0);
913     (void) response();
914 }
915
916
917
918 int response()
919 {
920     char resp, c, rbuf[RCP_BUFSIZ], *cp = rbuf;
921     if (rcmd_stream_read(rem, &resp, 1, 0) != 1)
922       lostconn(0);
923     switch (resp) {
924         
925       case 0:                           /* ok */
926         return (0);
927         
928       default:
929         *cp++ = resp;
930         /* fall into... */
931       case 1:                           /* error, followed by err msg */
932       case 2:                           /* fatal error, "" */
933         do {
934             if (rcmd_stream_read(rem, &c, 1, 0) != 1)
935               lostconn(0);
936             *cp++ = c;
937         } while (cp < &rbuf[RCP_BUFSIZ] && c != '\n');
938         if (iamremote == 0)
939           (void) write(2, rbuf, (unsigned) (cp - rbuf));
940         errs++;
941         if (resp == 1)
942           return (-1);
943         exit(1);
944     }
945     /*NOTREACHED*/
946 }
947
948
949
950 krb5_sigtype
951   lostconn(signumber)
952     int signumber;
953 {
954     if (iamremote == 0)
955       fprintf(stderr, "rcp: lost connection\n");
956     exit(1);
957 }
958
959
960 #if !defined(HAVE_UTIMES)
961 #include <utime.h>
962 #include <sys/time.h>
963
964 /*
965  * We emulate utimes() instead of utime() as necessary because
966  * utimes() is more powerful than utime(), and rcp actually tries to
967  * set the microsecond values; we don't want to take away
968  * functionality unnecessarily.
969  */
970 int utimes(file, tvp)
971 const char *file;
972 struct timeval *tvp;
973 {
974         struct utimbuf times;
975
976         times.actime = tvp[0].tv_sec;
977         times.modtime = tvp[1].tv_sec;
978         return(utime(file, &times));
979 }
980 #endif
981
982
983 void sink(argc, argv)
984      int argc;
985      char **argv;
986 {
987     mode_t mode;
988     mode_t mask = umask(0);
989     off_t i, j;
990     char *targ, *whopp, *cp;
991     int of, wrerr, exists, first;
992     off_t size;
993     unsigned int amt, count;
994     struct buffer *bp;
995     static struct buffer buffer;
996     struct stat stb;
997     int targisdir = 0;
998     char *myargv[1];
999     char cmdbuf[RCP_BUFSIZ], nambuf[RCP_BUFSIZ];
1000     int setimes = 0;
1001     struct timeval tv[2];
1002 #define atime   tv[0]
1003 #define mtime   tv[1]
1004 #define SCREWUP(str)    { whopp = str; goto screwup; }
1005     
1006     if (!pflag)
1007       (void) umask(mask);
1008     if (argc != 1) {
1009         error("rcp: ambiguous target\n");
1010         exit(1);
1011     }
1012     targ = *argv;
1013     if (targetshouldbedirectory)
1014       verifydir(targ);
1015     ga();
1016     if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
1017       targisdir = 1;
1018     for (first = 1; ; first = 0) {
1019         cp = cmdbuf;
1020         if (rcmd_stream_read(rem, cp, 1, 0) <= 0)
1021           return;
1022         if (*cp++ == '\n')
1023           SCREWUP("unexpected '\\n'");
1024         do {
1025             if (rcmd_stream_read(rem, cp, 1, 0) != 1)
1026               SCREWUP("lost connection");
1027         } while (*cp++ != '\n');
1028         *cp = 0;
1029         if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
1030             if (iamremote == 0)
1031               (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
1032             if (cmdbuf[0] == '\02')
1033               exit(1);
1034             errs++;
1035             continue;
1036         }
1037         *--cp = 0;
1038         cp = cmdbuf;
1039         if (*cp == 'E') {
1040             ga();
1041             return;
1042         }
1043         
1044 #define getnum(t) (t) = 0; while (isdigit((int) *cp)) (t) = (t) * 10 + (*cp++ - '0');
1045         if (*cp == 'T') {
1046             setimes++;
1047             cp++;
1048             getnum(mtime.tv_sec);
1049             if (*cp++ != ' ')
1050               SCREWUP("mtime.sec not delimited");
1051             getnum(mtime.tv_usec);
1052             if (*cp++ != ' ')
1053               SCREWUP("mtime.usec not delimited");
1054             getnum(atime.tv_sec);
1055             if (*cp++ != ' ')
1056               SCREWUP("atime.sec not delimited");
1057             getnum(atime.tv_usec);
1058             if (*cp++ != '\0')
1059               SCREWUP("atime.usec not delimited");
1060             ga();
1061             continue;
1062         }
1063         if (*cp != 'C' && *cp != 'D') {
1064             /*
1065              * Check for the case "rcp remote:foo\* local:bar".
1066              * In this case, the line "No match." can be returned
1067              * by the shell before the rcp command on the remote is
1068              * executed so the ^Aerror_message convention isn't
1069              * followed.
1070              */
1071             if (first) {
1072                 error("%s\n", cp);
1073                 exit(1);
1074             }
1075             SCREWUP("expected control record");
1076         }
1077         cp++;
1078         mode = 0;
1079         for (; cp < cmdbuf+5; cp++) {
1080             if (*cp < '0' || *cp > '7')
1081               SCREWUP("bad mode");
1082             mode = (mode << 3) | (*cp - '0');
1083         }
1084         if (*cp++ != ' ')
1085           SCREWUP("mode not delimited");
1086         size = 0;
1087         while (isdigit((int) *cp))
1088           size = size * 10 + (*cp++ - '0');
1089         if (*cp++ != ' ')
1090           SCREWUP("size not delimited");
1091         if (targisdir) {
1092           if(strlen(targ) + strlen(cp) + 2 >= sizeof(nambuf))
1093             SCREWUP("target name too long");
1094           (void) sprintf(nambuf, "%s%s%s", targ,
1095                          *targ ? "/" : "", cp);
1096         } else {
1097           if (strlen(targ) + 1 >= sizeof (nambuf))
1098             SCREWUP("target name too long");
1099           (void) strncpy(nambuf, targ, sizeof(nambuf) - 1);
1100         }
1101         nambuf[sizeof(nambuf) - 1] = '\0';
1102         exists = stat(nambuf, &stb) == 0;
1103         if (cmdbuf[0] == 'D') {
1104             if (exists) {
1105                 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
1106                     errno = ENOTDIR;
1107                     goto bad;
1108                 }
1109                 if (pflag)
1110                   (void) chmod(nambuf, mode);
1111             } else if (mkdir(nambuf, mode) < 0)
1112               goto bad;
1113             myargv[0] = nambuf;
1114             sink(1, myargv);
1115             if (setimes) {
1116                 setimes = 0;
1117                 if (utimes(nambuf, tv) < 0)
1118                   error("rcp: can't set times on %s: %s\n",
1119                         nambuf, error_message(errno));
1120             }
1121             continue;
1122         }
1123         if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
1124           bad:
1125             error("rcp: %s: %s\n", nambuf, error_message(errno));
1126             continue;
1127         }
1128         if (exists && pflag) {
1129 #ifdef NOFCHMOD
1130             (void) chmod(nambuf, mode);
1131 #else
1132             (void) fchmod(of, mode);
1133 #endif
1134         }
1135         ga();
1136         if ((bp = allocbuf(&buffer, of, RCP_BUFSIZ)) == NULLBUF) {
1137             (void) close(of);
1138             continue;
1139         }
1140         cp = bp->buf;
1141         count = 0;
1142         wrerr = 0;
1143         for (i = 0; i < size; i += RCP_BUFSIZ) {
1144             amt = RCP_BUFSIZ;
1145             if (i + amt > size)
1146               amt = size - i;
1147             count += amt;
1148             do {
1149                 j = rcmd_stream_read(rem, cp, amt, 0);
1150                 if (j <= 0) {
1151                     if (j == 0)
1152                       error("rcp: dropped connection");
1153                     else
1154                       error("rcp: %s\n", error_message(errno));
1155                     exit(1);
1156                 }
1157                 amt -= j;
1158                 cp += j;
1159             } while (amt > 0);
1160             if (count == bp->cnt) {
1161                 if (wrerr == 0 &&
1162                     write(of, bp->buf, count) != count)
1163                   wrerr++;
1164                 count = 0;
1165                 cp = bp->buf;
1166             }
1167         }
1168         if (count != 0 && wrerr == 0 &&
1169             write(of, bp->buf, count) != count)
1170           wrerr++;
1171         if (ftruncate(of, size))
1172           error("rcp: can't truncate %s: %s\n", nambuf, error_message(errno));
1173         (void) close(of);
1174         (void) response();
1175         if (setimes) {
1176             setimes = 0;
1177             if (utimes(nambuf, tv) < 0)
1178               error("rcp: can't set times on %s: %s\n",
1179                     nambuf, error_message(errno));
1180         }                                  
1181         if (wrerr)
1182           error("rcp: %s: %s\n", nambuf, error_message(errno));
1183         else
1184           ga();
1185     }
1186   screwup:
1187     error("rcp: protocol screwup: %s\n", whopp);
1188     exit(1);
1189 }
1190
1191
1192
1193 struct buffer *allocbuf(bp, fd, blksize)
1194      struct buffer *bp;
1195      int fd, blksize;
1196 {
1197     struct stat stb;
1198     int size;
1199     
1200     if (fstat(fd, &stb) < 0) {
1201         error("rcp: fstat: %s\n", error_message(errno));
1202         return (NULLBUF);
1203     }
1204
1205     size = blksize;
1206     if (bp->cnt < size) {
1207         if (bp->buf != 0)
1208           free(bp->buf);
1209         bp->buf = (char *)malloc((unsigned) size);
1210         if (bp->buf == 0) {
1211             error("rcp: malloc: out of memory\n");
1212             return (NULLBUF);
1213         }
1214     }
1215     bp->cnt = size;
1216     return (bp);
1217 }
1218
1219 void
1220 #ifdef HAVE_STDARG_H
1221 error(char *fmt, ...)
1222 #else
1223 /*VARARGS1*/
1224 error(fmt, va_alist)
1225      char *fmt;
1226      va_dcl
1227 #endif
1228 {
1229     va_list ap;
1230     char buf[RCP_BUFSIZ], *cp = buf;
1231
1232 #ifdef HAVE_STDARG_H
1233     va_start(ap, fmt);
1234 #else
1235     va_start(ap);
1236 #endif
1237
1238     errs++;
1239     *cp++ = 1;
1240     (void) vsprintf(cp, fmt, ap);
1241     va_end(ap);
1242
1243     if (iamremote)
1244       (void) rcmd_stream_write(rem, buf, strlen(buf), 0);
1245     else
1246       (void) write(2, buf+1, strlen(buf+1));
1247 }
1248
1249
1250
1251 void usage()
1252 {
1253 #ifdef KERBEROS
1254     fprintf(stderr,
1255             "Usage: \trcp [-PN | -PO] [-p] [-x] [-k realm] f1 f2; or:\n\trcp [-PN | -PO] [-r] [-p] [-x] [-k realm] f1 ... fn d2\n");
1256 #else
1257     fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr);
1258 #endif
1259     exit(1);
1260 }
1261
1262
1263
1264 int hosteq(h1, h2)
1265      char *h1, *h2;
1266 {
1267     struct hostent *h_ptr;
1268     char hname1[256];
1269     
1270     if (forcenet)
1271       return(0);
1272
1273     /* get the official names for the two hosts */
1274     
1275     if ((h_ptr = gethostbyname(h1)) == NULL)
1276       return(0);
1277     strncpy(hname1, h_ptr->h_name, sizeof (hname1));
1278     hname1[sizeof (hname1) - 1] = '\0';
1279     if ((h_ptr = gethostbyname(h2)) == NULL)
1280       return(0);
1281     
1282     /*return if they are equal (strcmp returns 0 for equal - I return 1) */
1283     
1284     return(!strcmp(hname1, h_ptr->h_name));
1285 }
1286
1287
1288
1289 #ifdef KERBEROS
1290 void try_normal(argv)
1291      char **argv;
1292 {
1293     register int i;
1294 #ifndef     KRB5_ATHENA_COMPAT
1295     if (!encryptflag)
1296 #endif
1297         {
1298         fprintf(stderr,"trying normal rcp (%s)\n", UCB_RCP);
1299         fflush(stderr);
1300         /* close all but stdin, stdout, stderr */
1301         for (i = getdtablesize(); i > 2; i--)
1302           (void) close(i);
1303         execv(UCB_RCP, argv);
1304         perror("exec");
1305     }
1306     exit(1);
1307 }
1308
1309
1310
1311 char **save_argv(argc, argv)
1312      int argc;
1313      char **argv;
1314 {
1315     register int i;
1316     
1317     char **local_argv = (char **)calloc((unsigned) argc+1,
1318                                         (unsigned) sizeof(char *));
1319     /* allocate an extra pointer, so that it is initialized to NULL
1320        and execv() will work */
1321     for (i = 0; i < argc; i++)
1322       local_argv[i] = strsave(argv[i]);
1323     return(local_argv);
1324 }
1325
1326
1327
1328 #ifdef unicos61
1329 #define SIZEOF_INADDR  SIZEOF_in_addr
1330 #else
1331 #define SIZEOF_INADDR sizeof(struct in_addr)
1332 #endif
1333
1334
1335 /* This function is mostly vestigial, since under normal operation
1336  * the -x flag doesn't get set for the server process for encrypted
1337  * rcp.  It only gets called by beta clients attempting user-to-user
1338  * authentication. */
1339 void
1340   answer_auth(config_file, ccache_file)
1341     char *config_file;
1342     char *ccache_file;
1343 {
1344     krb5_data pname_data, msg;
1345     krb5_creds creds, *new_creds;
1346     krb5_ccache cc;
1347     krb5_error_code status;
1348     krb5_auth_context auth_context = NULL;
1349     
1350     if (config_file) {
1351         const char * filenames[2];
1352         filenames[1] = NULL;
1353         filenames[0] = config_file;
1354         if ((status = krb5_set_config_files(bsd_context, filenames)))
1355             exit(1);
1356     }
1357     
1358     memset ((char*)&creds, 0, sizeof(creds));
1359
1360     if ((status = krb5_read_message(bsd_context, (krb5_pointer)&rem,
1361                                     &pname_data)))
1362         exit(1);
1363     
1364     if ((status = krb5_read_message(bsd_context, (krb5_pointer) &rem,
1365                                     &creds.second_ticket)))
1366         exit(1);
1367     
1368     if (ccache_file == NULL) {
1369         if ((status = krb5_cc_default(bsd_context, &cc)))
1370             exit(1);
1371     } else {
1372         if ((status = krb5_cc_resolve(bsd_context, ccache_file, &cc)))
1373             exit(1);
1374     }
1375
1376     if ((status = krb5_cc_get_principal(bsd_context, cc, &creds.client)))
1377         exit(1);
1378
1379     if ((status = krb5_parse_name(bsd_context, pname_data.data,
1380                                   &creds.server)) )
1381         exit(1);
1382
1383     krb5_free_data_contents(bsd_context, &pname_data);
1384
1385     if ((status = krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, 
1386                                        &creds, &new_creds)))
1387         exit(1);
1388
1389     if ((status = krb5_mk_req_extended(bsd_context, &auth_context,
1390                                        AP_OPTS_USE_SESSION_KEY,
1391                                        NULL, new_creds, &msg)))
1392         exit(1);
1393     
1394     if ((status = krb5_write_message(bsd_context, (krb5_pointer) &rem,
1395                                      &msg))) {
1396         krb5_free_data_contents(bsd_context, &msg);
1397         exit(1);
1398     }
1399     
1400     rcmd_stream_init_krb5(&new_creds->keyblock, encryptflag, 0, 0,
1401                           KCMD_OLD_PROTOCOL);
1402     
1403     /* cleanup */
1404     krb5_free_cred_contents(bsd_context, &creds);
1405     krb5_free_creds(bsd_context, new_creds);
1406     krb5_free_data_contents(bsd_context, &msg);
1407
1408     return;
1409 }
1410
1411
1412
1413 char storage[2*RCP_BUFSIZ];             /* storage for the decryption */
1414 int nstored = 0;
1415 char *store_ptr = storage;
1416
1417 #ifdef KRB5_KRB4_COMPAT
1418 void
1419 v4_send_auth(host,realm)
1420 char *host;
1421 char *realm;
1422 {
1423         long authopts;
1424
1425         if ((realm == NULL) || (realm[0] == '\0'))
1426              realm = krb_realmofhost(host);
1427         /* this needs to be sent again, because the
1428            rcp process needs the key.  the rshd has
1429            grabbed the first one. */
1430         authopts = KOPT_DO_MUTUAL;
1431         if ((rem = krb_sendauth(authopts, sock, &v4_ticket,
1432                                 "rcmd", host,
1433                                 realm, (unsigned long) getpid(),
1434                                 &v4_msg_data,
1435                                 &v4_cred, v4_schedule,
1436                                 &local,
1437                                 &foreign,
1438                                 "KCMDV0.1")) != KSUCCESS) {
1439                 fprintf(stderr,
1440                         "krb_sendauth mutual fail: %s\n",
1441                         krb_get_err_text(rem));
1442                 exit(1);
1443         }
1444 }
1445 #endif /* KRB5_KRB4_COMPAT */
1446
1447 #endif /* KERBEROS */