8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / rcp.c
blob0bbcc2f2fbf74166be2fabac992987f334fbe28b
1 /*
2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 1983 The Regents of the University of California.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
21 #define _FILE_OFFSET_BITS 64
24 * rcp
26 #include <sys/param.h>
27 #include <sys/file.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/acl.h>
33 #include <dirent.h>
34 #include <signal.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <pwd.h>
38 #include <netdb.h>
39 #include <wchar.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <locale.h>
43 #include <strings.h>
44 #include <stdio.h>
45 #include <ctype.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <limits.h>
49 #include <priv_utils.h>
50 #include <sys/sendfile.h>
51 #include <sys/sysmacros.h>
52 #include <sys/wait.h>
53 #include <aclutils.h>
54 #include <sys/varargs.h>
57 * It seems like Berkeley got these from pathnames.h?
59 #define _PATH_RSH "/usr/bin/rsh"
60 #define _PATH_CP "/usr/bin/cp"
62 #define ACL_FAIL 1
63 #define ACL_OK 0
64 #define RCP_BUFSIZE (64 * 1024)
66 #define RCP_ACL "/usr/lib/sunw,rcp"
67 /* see PSARC/1993/004/opinion */
69 typedef struct _buf {
70 int cnt;
71 char *buf;
72 } BUF;
74 static char *cmd_sunw;
75 static struct passwd *pwd;
76 static int errs;
77 static int pflag;
78 static uid_t userid;
79 static int rem;
80 static int zflag;
81 static int iamremote;
82 static int iamrecursive;
83 static int targetshouldbedirectory;
84 static int aclflag;
85 static int acl_aclflag;
86 static int retval = 0;
87 static int portnumber = 0;
89 static void lostconn(void);
90 static char *search_char(unsigned char *, unsigned char);
91 static char *removebrackets(char *);
92 static char *colon(char *);
93 static int response(void);
94 static void usage(void);
95 static void source(int, char **);
96 static void sink(int, char **);
97 static void toremote(char *, int, char **);
98 static void tolocal(int, char **);
99 static void verifydir(char *);
100 static int okname(char *);
101 static int susystem(char *, char **);
102 static void rsource(char *, struct stat *);
103 static int sendacl(int);
104 static int recvacl(int, int, int);
105 static int zwrite(int, char *, int);
106 static void zopen(int, int);
107 static int zclose(int);
108 static int notzero(char *, int);
109 static BUF *allocbuf(BUF *, int, int);
110 static void error(char *fmt, ...);
111 static void addargs(char **, ...);
114 * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF
115 * bytes of data. We would like the size to be aligned to the nearest
116 * MAXBOFFSET (8192) boundary for optimal performance.
118 #define SENDFILE_SIZE 0x7FFFE000
120 #include <k5-int.h>
121 #include <profile/prof_int.h>
122 #include <com_err.h>
123 #include <kcmd.h>
125 #define NULLBUF (BUF *) 0
126 #define MAXARGS 10 /* Number of arguments passed to execv() */
128 static int sock;
129 static char *cmd, *cmd_orig, *cmd_sunw_orig;
130 static char *krb_realm = NULL;
131 static char *krb_cache = NULL;
132 static char *krb_config = NULL;
133 static char des_inbuf[2 * RCP_BUFSIZE];
134 /* needs to be > largest read size */
135 static char des_outbuf[2 * RCP_BUFSIZE];
136 /* needs to be > largest write size */
138 static krb5_data desinbuf, desoutbuf;
139 static krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
140 static krb5_keyblock *session_key; /* static key for session */
141 static krb5_context bsd_context = NULL;
142 static krb5_auth_context auth_context;
143 static krb5_flags authopts;
144 static krb5_error_code status;
146 static void try_normal_rcp(int, char **);
147 static int init_service(int);
148 static char **save_argv(int, char **);
149 static void answer_auth(char *, char *);
150 static int desrcpwrite(int, char *, int);
151 static int desrcpread(int, char *, int);
154 * Not sure why these two don't have their own header file declarations, but
155 * lint complains about absent declarations so place some here. Sigh.
157 extern errcode_t profile_get_options_boolean(profile_t, char **,
158 profile_options_boolean *);
159 extern errcode_t profile_get_options_string(profile_t, char **,
160 profile_option_strings *);
162 static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */
163 static profile_options_boolean autologin_option[] = {
164 { "autologin", &krb5auth_flag, 0 },
165 { NULL, NULL, 0 }
167 static int no_krb5auth_flag = 0;
169 static int encrypt_flag = 0; /* Flag set, when encryption is enabled */
170 static int encrypt_done = 0; /* Flag set, if "-x" is specified */
171 static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
173 /* Flag set, if -PN / -PO is specified */
174 static boolean_t rcmdoption_done = B_FALSE;
176 static profile_options_boolean option[] = {
177 { "encrypt", &encrypt_flag, 0 },
178 { NULL, NULL, 0 }
181 static char *rcmdproto = NULL;
182 static profile_option_strings rcmdversion[] = {
183 { "rcmd_protocol", &rcmdproto, 0 },
184 { NULL, NULL, 0 }
187 static char *realmdef[] = { "realms", NULL, "rcp", NULL };
188 static char *appdef[] = { "appdefaults", "rcp", NULL };
189 static char **prev_argv;
190 static int prev_argc;
193 main(int argc, char *argv[])
195 int ch, fflag, tflag;
196 char *targ;
197 size_t cmdsiz;
199 (void) setlocale(LC_ALL, "");
201 if (strcmp(argv[0], RCP_ACL) == 0)
202 aclflag = 1;
204 if (!(pwd = getpwuid(userid = getuid()))) {
205 (void) fprintf(stderr, "rcp: unknown user %d.\n",
206 (uint_t)userid);
207 return (1);
210 fflag = tflag = 0;
211 while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:ZK")) != EOF) {
212 switch (ch) {
213 case 'd':
214 targetshouldbedirectory = 1;
215 break;
216 case 'f': /* "from" */
217 fflag = 1;
218 if (aclflag | acl_aclflag)
219 /* ok response */
220 (void) desrcpwrite(rem, "", 1);
221 break;
222 case 'p': /* preserve access/mod times */
223 ++pflag;
224 break;
225 case 'r':
226 ++iamrecursive;
227 break;
228 case 't': /* "to" */
229 tflag = 1;
230 break;
231 case 'Z':
232 acl_aclflag++;
233 break;
234 case 'K':
235 no_krb5auth_flag++;
236 break;
237 case 'x':
238 if (!krb5_privacy_allowed()) {
239 (void) fprintf(stderr, gettext("rcp: "
240 "Encryption not supported.\n"));
241 return (1);
243 encrypt_flag++;
244 krb5auth_flag++;
245 encrypt_done++;
246 break;
247 case 'k':
248 if ((krb_realm = (char *)strdup(optarg)) == NULL) {
249 (void) fprintf(stderr, gettext("rcp:"
250 " Cannot malloc.\n"));
251 return (1);
253 krb5auth_flag++;
254 break;
255 case 'P':
256 if (strncmp(optarg, "O", 1) == 0) {
257 if (rcmdoption_done == B_TRUE) {
258 (void) fprintf(stderr, gettext("rcp: "
259 "Only one of -PN and -PO "
260 "allowed.\n"));
261 usage();
263 kcmd_proto = KCMD_OLD_PROTOCOL;
264 rcmdoption_done = B_TRUE;
265 } else if (strncmp(optarg, "N", 1) == 0) {
266 if (rcmdoption_done == B_TRUE) {
267 (void) fprintf(stderr, gettext("rcp: "
268 "Only one of -PN and -PO "
269 "allowed.\n"));
270 usage();
272 kcmd_proto = KCMD_NEW_PROTOCOL;
273 rcmdoption_done = B_TRUE;
274 } else {
275 usage();
277 krb5auth_flag++;
278 break;
279 case 'a':
280 krb5auth_flag++;
281 break;
282 #ifdef DEBUG
283 case 'D':
284 portnumber = htons(atoi(optarg));
285 krb5auth_flag++;
286 break;
287 #endif /* DEBUG */
288 case '?':
289 default:
290 usage();
293 argc -= optind;
294 argv += optind;
297 * if the user disables krb5 on the cmdline (-K), then skip
298 * all krb5 setup.
300 * if the user does not disable krb5 or enable krb5 on the
301 * cmdline, check krb5.conf to see if it should be enabled.
304 if (no_krb5auth_flag) {
305 krb5auth_flag = 0;
306 fflag = encrypt_flag = 0;
307 } else if (!krb5auth_flag) {
308 /* is autologin set in krb5.conf? */
309 status = krb5_init_context(&bsd_context);
310 /* don't sweat failure here */
311 if (!status) {
313 * note that the call to profile_get_options_boolean
314 * with autologin_option can affect value of
315 * krb5auth_flag
317 (void) profile_get_options_boolean(bsd_context->profile,
318 appdef,
319 autologin_option);
323 if (krb5auth_flag > 0) {
324 if (!bsd_context) {
325 status = krb5_init_context(&bsd_context);
326 if (status) {
327 com_err("rcp", status,
328 gettext("while initializing krb5"));
329 return (1);
334 * Set up buffers for desread and deswrite.
336 desinbuf.data = des_inbuf;
337 desoutbuf.data = des_outbuf;
338 desinbuf.length = sizeof (des_inbuf);
339 desoutbuf.length = sizeof (des_outbuf);
342 if (fflag || tflag)
343 if (encrypt_flag > 0)
344 (void) answer_auth(krb_config, krb_cache);
346 if (fflag) {
347 iamremote = 1;
348 (void) response();
349 (void) setuid(userid);
350 source(argc, argv);
351 return (errs);
354 if (tflag) {
355 iamremote = 1;
356 (void) setuid(userid);
357 sink(argc, argv);
358 return (errs);
361 if (argc < 2)
362 usage();
364 /* This will make "rcmd_af()" magically get the proper privilege */
365 if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) {
366 (void) fprintf(stderr, "rcp: must be set-uid root\n");
367 exit(1);
370 if (krb5auth_flag > 0) {
372 * Get our local realm to look up local realm options.
374 status = krb5_get_default_realm(bsd_context, &realmdef[1]);
375 if (status) {
376 com_err("rcp", status,
377 gettext("while getting default realm"));
378 return (1);
381 * See if encryption should be done for this realm
383 (void) profile_get_options_boolean(bsd_context->profile,
384 realmdef, option);
386 * Check the appdefaults section
388 (void) profile_get_options_boolean(bsd_context->profile,
389 appdef, option);
390 (void) profile_get_options_string(bsd_context->profile,
391 appdef, rcmdversion);
392 if ((encrypt_done > 0) || (encrypt_flag > 0)) {
393 if (krb5_privacy_allowed() == TRUE) {
394 encrypt_flag++;
395 } else {
396 (void) fprintf(stderr, gettext("rcp: Encryption"
397 " not supported.\n"));
398 return (1);
402 if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
403 if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
404 kcmd_proto = KCMD_NEW_PROTOCOL;
405 } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
406 kcmd_proto = KCMD_OLD_PROTOCOL;
407 } else {
408 (void) fprintf(stderr, gettext("Unrecognized "
409 "KCMD protocol (%s)"), rcmdproto);
410 return (1);
415 if (argc > 2)
416 targetshouldbedirectory = 1;
418 rem = -1;
420 if (portnumber == 0) {
421 if (krb5auth_flag > 0) {
422 retval = init_service(krb5auth_flag);
423 if (!retval) {
425 * Connecting to the kshell service failed,
426 * fallback to normal rcp & reset KRB5 flags.
428 krb5auth_flag = encrypt_flag = 0;
429 encrypt_done = 0;
430 (void) init_service(krb5auth_flag);
433 else
434 (void) init_service(krb5auth_flag);
437 #ifdef DEBUG
438 if (retval || krb5auth_flag) {
439 (void) fprintf(stderr, gettext("Kerberized rcp session, "
440 "port %d in use "), portnumber);
441 if (kcmd_proto == KCMD_OLD_PROTOCOL)
442 (void) fprintf(stderr, gettext("[kcmd ver.1]\n"));
443 else
444 (void) fprintf(stderr, gettext("[kcmd ver.2]\n"));
445 } else {
446 (void) fprintf(stderr, gettext("Normal rcp session, port %d "
447 "in use.\n"), portnumber);
449 #endif /* DEBUG */
451 if (krb5auth_flag > 0) {
453 * We calculate here a buffer size that can be used in the
454 * allocation of the three buffers cmd, cmd_orig and
455 * cmd_sunw_orig that are used to hold different incantations
456 * of rcp.
458 cmdsiz = MAX(sizeof ("-x rcp -r -p -d -k ") +
459 strlen(krb_realm != NULL ? krb_realm : ""),
460 sizeof (RCP_ACL " -r -p -z -d"));
462 if (((cmd = (char *)malloc(cmdsiz)) == NULL) ||
463 ((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) ||
464 ((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) {
465 (void) fprintf(stderr, gettext("rcp: Cannot "
466 "malloc.\n"));
467 return (1);
470 (void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s",
471 encrypt_flag ? "-x " : "",
472 iamrecursive ? " -r" : "", pflag ? " -p" : "",
473 targetshouldbedirectory ? " -d" : "",
474 krb_realm != NULL ? " -k " : "",
475 krb_realm != NULL ? krb_realm : "");
478 * We would use cmd-orig as the 'cmd-buffer' if kerberized
479 * rcp fails, in which case we fallback to normal rcp. We also
480 * save argc & argv for the same purpose
482 (void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s",
483 iamrecursive ? " -r" : "",
484 pflag ? " -p" : "",
485 zflag ? " -z" : "",
486 targetshouldbedirectory ? " -d" : "");
488 (void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL,
489 iamrecursive ? " -r" : "",
490 pflag ? " -p" : "",
491 zflag ? " -z" : "",
492 targetshouldbedirectory ? " -d" : "");
494 prev_argc = argc;
495 prev_argv = save_argv(argc, argv);
497 } else {
498 cmdsiz = sizeof ("rcp -r -p -z -d");
499 if (((cmd = (char *)malloc(cmdsiz)) == NULL)) {
500 (void) fprintf(stderr, gettext("rcp: Cannot "
501 "malloc.\n"));
502 return (1);
505 (void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s",
506 iamrecursive ? " -r" : "",
507 pflag ? " -p" : "",
508 zflag ? " -z" : "",
509 targetshouldbedirectory ? " -d" : "");
512 cmdsiz = sizeof (RCP_ACL " -r -p -z -d");
513 if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) {
514 (void) fprintf(stderr, gettext("rcp: Cannot malloc.\n"));
515 return (1);
518 (void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL,
519 iamrecursive ? " -r" : "",
520 pflag ? " -p" : "",
521 zflag ? " -z" : "",
522 targetshouldbedirectory ? " -d" : "");
524 (void) signal(SIGPIPE, (void (*)(int))lostconn);
526 if (targ = colon(argv[argc - 1]))
527 toremote(targ, argc, argv);
528 else {
529 tolocal(argc, argv);
530 if (targetshouldbedirectory)
531 verifydir(argv[argc - 1]);
534 return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
538 static void
539 toremote(char *targ, int argc, char *argv[])
541 int i;
542 char *host, *src, *suser, *thost, *tuser;
543 char resp;
544 size_t buffersize;
545 char bp[RCP_BUFSIZE];
546 krb5_creds *cred;
547 char *arglist[MAXARGS+1];
548 buffersize = RCP_BUFSIZE;
550 *targ++ = 0;
551 if (*targ == 0)
552 targ = ".";
554 if (thost = search_char((unsigned char *)argv[argc - 1], '@')) {
555 *thost++ = 0;
556 tuser = argv[argc - 1];
557 if (*tuser == '\0')
558 tuser = NULL;
559 else if (!okname(tuser))
560 exit(1);
561 } else {
562 thost = argv[argc - 1];
563 tuser = NULL;
565 thost = removebrackets(thost);
567 for (i = 0; i < argc - 1; i++) {
568 src = colon(argv[i]);
569 if (src) { /* remote to remote */
570 *src++ = 0;
571 if (*src == 0)
572 src = ".";
573 host = search_char((unsigned char *)argv[i], '@');
574 if (host) {
575 *host++ = 0;
576 host = removebrackets(host);
577 suser = argv[i];
578 if (*suser == '\0') {
579 suser = pwd->pw_name;
580 } else if (!okname(suser)) {
581 errs++;
582 continue;
584 (void) snprintf(bp, buffersize, "'%s%s%s:%s'",
585 tuser ? tuser : "", tuser ? "@" : "",
586 thost, targ);
587 (void) addargs(arglist, "rsh", host, "-l",
588 suser, "-n", cmd, src, bp, (char *)NULL);
589 } else {
590 host = removebrackets(argv[i]);
591 (void) snprintf(bp, buffersize, "'%s%s%s:%s'",
592 tuser ? tuser : "", tuser ? "@" : "",
593 thost, targ);
594 (void) addargs(arglist, "rsh", host, "-n", cmd,
595 src, bp, (char *)NULL);
597 if (susystem(_PATH_RSH, arglist) == -1)
598 errs++;
599 } else { /* local to remote */
600 if (rem == -1) {
601 host = thost;
602 if (krb5auth_flag > 0) {
604 (void) snprintf(bp, buffersize,
605 "%s -t %s", cmd, targ);
606 authopts = AP_OPTS_MUTUAL_REQUIRED;
607 status = kcmd(&sock, &host,
608 portnumber,
609 pwd->pw_name,
610 tuser ? tuser :
611 pwd->pw_name,
614 "host",
615 krb_realm,
616 bsd_context,
617 &auth_context,
618 &cred,
619 0, /* No seq # */
620 0, /* No server seq # */
621 authopts,
622 0, /* Not any port # */
623 &kcmd_proto);
624 if (status) {
626 * If new protocol requested, we dont
627 * fallback to less secure ones.
630 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
631 (void) fprintf(stderr,
632 gettext("rcp: kcmdv2 "
633 "to host %s failed - %s"
634 "\nFallback to normal "
635 "rcp denied."), host,
636 error_message(status));
637 exit(1);
639 if (status != -1) {
640 (void) fprintf(stderr,
641 gettext("rcp: kcmd to host "
642 "%s failed - %s,\n"
643 "trying normal rcp...\n\n"),
644 host,
645 error_message(status));
646 } else {
647 (void) fprintf(stderr,
648 gettext("trying normal"
649 " rcp...\n"));
652 * kcmd() failed, so we have to
653 * fallback to normal rcp
655 try_normal_rcp(prev_argc, prev_argv);
656 } else {
657 rem = sock;
658 session_key = &cred->keyblock;
659 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
660 /* CSTYLED */
661 status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key);
662 if (status) {
663 com_err("rcp", status,
664 "determining "
665 "subkey for "
666 "session");
667 exit(1);
669 if (!session_key) {
670 com_err("rcp", 0,
671 "no subkey "
672 "negotiated for"
673 " connection");
674 exit(1);
677 eblock.crypto_entry =
678 session_key->enctype;
679 eblock.key =
680 (krb5_keyblock *)session_key;
682 init_encrypt(encrypt_flag,
683 bsd_context, kcmd_proto,
684 &desinbuf, &desoutbuf, CLIENT,
685 &eblock);
686 if (encrypt_flag > 0) {
687 char *s = gettext("This rcp "
688 "session is using "
689 "encryption for all "
690 "data transmissions."
691 "\r\n");
693 (void) write(2, s, strlen(s));
696 if (response() < 0)
697 exit(1);
699 } else {
702 * ACL support: try to find out if the
703 * remote site is running acl cognizant
704 * version of rcp. A special binary
705 * name is used for this purpose.
707 aclflag = 1;
708 acl_aclflag = 1;
711 * First see if the remote side will
712 * support both aclent_t and ace_t
713 * acl's?
715 (void) snprintf(bp, buffersize,
716 "%s -tZ %s",
717 cmd_sunw, targ);
718 rem = rcmd_af(&host, portnumber,
719 pwd->pw_name,
720 tuser ? tuser : pwd->pw_name,
721 bp, 0, AF_INET6);
722 if (rem < 0)
723 exit(1);
726 * This is similar to routine
727 * response(). If response is not ok,
728 * treat the other side as non-acl rcp.
730 if (read(rem, &resp, sizeof (resp))
731 != sizeof (resp))
732 lostconn();
733 if (resp != 0) {
734 acl_aclflag = 0;
735 (void) snprintf(bp, buffersize,
736 "%s -t %s", cmd_sunw, targ);
738 (void) close(rem);
739 host = thost;
740 rem = rcmd_af(&host, portnumber,
741 pwd->pw_name,
742 tuser ? tuser :
743 pwd->pw_name,
744 bp, 0, AF_INET6);
745 if (rem < 0)
746 exit(1);
748 if (read(rem, &resp,
749 sizeof (resp))
750 != sizeof (resp))
751 lostconn();
752 if (resp != 0) {
754 * Not OK:
755 * The other side is
756 * running non-acl rcp.
757 * Try again with
758 * normal stuff.
760 aclflag = 0;
761 (void) snprintf(bp,
762 buffersize,
763 "%s -t %s", cmd,
764 targ);
765 (void) close(rem);
766 host = thost;
767 rem = rcmd_af(&host,
768 portnumber,
769 pwd->pw_name,
770 tuser ? tuser :
771 pwd->pw_name, bp, 0,
772 AF_INET6);
773 if (rem < 0)
774 exit(1);
775 if (response() < 0)
776 exit(1);
779 /* everything should be fine now */
780 (void) setuid(userid);
784 source(1, argv + i);
789 static void
790 tolocal(int argc, char *argv[])
792 int i;
793 char *host, *src, *suser, *lhost;
794 char resp;
795 size_t buffersize;
796 char bp[RCP_BUFSIZE];
797 krb5_creds *cred;
798 char *arglist[MAXARGS+1];
799 buffersize = RCP_BUFSIZE;
801 for (i = 0; i < argc - 1; i++) {
802 if (!(src = colon(argv[i]))) { /* local to local */
803 (void) addargs(arglist, "cp",
804 iamrecursive ? "-r" : "", pflag ? "-p" : "",
805 zflag ? "-z" : "", argv[i], argv[argc - 1],
806 (char *)NULL);
807 if (susystem(_PATH_CP, arglist) == -1)
808 errs++;
809 continue;
811 *src++ = 0;
812 if (*src == 0)
813 src = ".";
814 host = search_char((unsigned char *)argv[i], '@');
815 if (host) {
816 *host++ = 0;
817 suser = argv[i];
818 if (*suser == '\0') {
819 suser = pwd->pw_name;
820 } else if (!okname(suser)) {
821 errs++;
822 continue;
824 } else {
825 host = argv[i];
826 suser = pwd->pw_name;
828 host = removebrackets(host);
829 lhost = host;
830 if (krb5auth_flag > 0) {
832 (void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
833 authopts = AP_OPTS_MUTUAL_REQUIRED;
834 status = kcmd(&sock, &host,
835 portnumber,
836 pwd->pw_name, suser,
838 0, /* &rfd2 */
839 "host",
840 krb_realm,
841 bsd_context,
842 &auth_context,
843 &cred,
844 0, /* No seq # */
845 0, /* No server seq # */
846 authopts,
847 1, /* Not any port # */
848 &kcmd_proto);
849 if (status) {
851 * If new protocol requested, we dont
852 * fallback to less secure ones.
854 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
855 (void) fprintf(stderr,
856 gettext("rcp: kcmdv2 "
857 "to host %s failed - %s\n"
858 "Fallback to normal rcp denied."),
859 host, error_message(status));
860 exit(1);
862 if (status != -1) {
863 (void) fprintf(stderr,
864 gettext("rcp: kcmd "
865 "to host %s failed - %s,\n"
866 "trying normal rcp...\n\n"),
867 host, error_message(status));
868 } else {
869 (void) fprintf(stderr,
870 gettext("trying normal rcp...\n"));
873 * kcmd() failed, so we have to
874 * fallback to normal rcp
876 try_normal_rcp(prev_argc, prev_argv);
877 } else {
878 rem = sock;
879 session_key = &cred->keyblock;
880 if (kcmd_proto == KCMD_NEW_PROTOCOL) {
881 status = krb5_auth_con_getlocalsubkey(
882 bsd_context, auth_context,
883 &session_key);
884 if (status) {
885 com_err("rcp", status,
886 "determining "
887 "subkey for session");
888 exit(1);
890 if (!session_key) {
891 com_err("rcp", 0,
892 "no subkey negotiated"
893 " for connection");
894 exit(1);
897 eblock.crypto_entry = session_key->enctype;
898 eblock.key = (krb5_keyblock *)session_key;
900 init_encrypt(encrypt_flag, bsd_context,
901 kcmd_proto,
902 &desinbuf, &desoutbuf, CLIENT,
903 &eblock);
904 if (encrypt_flag > 0) {
905 char *s = gettext("This rcp "
906 "session is using DES "
907 "encryption for all "
908 "data transmissions."
909 "\r\n");
911 (void) write(2, s, strlen(s));
916 else
920 * ACL support: try to find out if the remote site is
921 * running acl cognizant version of rcp.
923 aclflag = 1;
924 acl_aclflag = 1;
926 (void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw,
927 src);
928 rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
929 bp, 0, AF_INET6);
931 if (rem < 0) {
932 ++errs;
933 continue;
937 * The remote system is supposed to send an ok response.
938 * If there are any data other than "ok", it must be
939 * error messages from the remote system. We can assume
940 * the remote system is running non-acl version rcp.
942 if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
943 lostconn();
945 if (resp != 0) {
948 * Try again without ace_acl support
950 acl_aclflag = 0;
951 (void) snprintf(bp, buffersize, "%s -f %s",
952 cmd_sunw, src);
953 (void) close(rem);
954 rem = rcmd_af(&host, portnumber, pwd->pw_name,
955 suser, bp, 0, AF_INET6);
957 if (rem < 0) {
958 ++errs;
959 continue;
962 if (read(rem, &resp,
963 sizeof (resp)) != sizeof (resp))
964 lostconn();
966 if (resp != 0) {
968 * NOT ok:
969 * The other side is running non-acl
970 * rcp. Try again with normal stuff.
972 aclflag = 0;
973 (void) snprintf(bp, buffersize,
974 "%s -f %s", cmd, src);
975 (void) close(rem);
976 host = lhost;
977 rem = rcmd_af(&host, portnumber,
978 pwd->pw_name, suser, bp, 0,
979 AF_INET6);
980 if (rem < 0) {
981 ++errs;
982 continue;
988 sink(1, argv + argc - 1);
990 (void) close(rem);
991 rem = -1;
996 static void
997 verifydir(char *cp)
999 struct stat stb;
1001 if (stat(cp, &stb) >= 0) {
1002 if ((stb.st_mode & S_IFMT) == S_IFDIR)
1003 return;
1004 errno = ENOTDIR;
1006 error("rcp: %s: %s.\n", cp, strerror(errno));
1007 exit(1);
1010 static char *
1011 colon(char *cp)
1013 boolean_t is_bracket_open = B_FALSE;
1015 for (; *cp; ++cp) {
1016 if (*cp == '[')
1017 is_bracket_open = B_TRUE;
1018 else if (*cp == ']')
1019 is_bracket_open = B_FALSE;
1020 else if (*cp == ':' && !is_bracket_open)
1021 return (cp);
1022 else if (*cp == '/')
1023 return (0);
1025 return (0);
1028 static int
1029 okname(char *cp0)
1031 register char *cp = cp0;
1032 register int c;
1034 do {
1035 c = *cp;
1036 if (c & 0200)
1037 goto bad;
1038 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
1039 goto bad;
1040 } while (*++cp);
1041 return (1);
1042 bad:
1043 (void) fprintf(stderr, "rcp: invalid user name %s\n", cp0);
1044 return (0);
1048 static char *
1049 removebrackets(char *str)
1051 char *newstr = str;
1053 if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
1054 newstr = str + 1;
1055 str[strlen(str) - 1] = '\0';
1057 return (newstr);
1060 static int
1061 susystem(char *path, char **arglist)
1063 int status, pid, w;
1064 register void (*istat)(), (*qstat)();
1065 int pfds[2];
1066 char buf[BUFSIZ];
1067 int cnt;
1068 boolean_t seen_stderr_traffic;
1071 * Due to the fact that rcp uses rsh to copy between 2 remote
1072 * machines, rsh doesn't return the exit status of the remote
1073 * command, and we can't modify the rcmd protocol used by rsh
1074 * (for interoperability reasons) we use the hack of using any
1075 * output on stderr as indication that an error occurred and
1076 * that we should return a non-zero error code.
1079 if (pipe(pfds) == -1) {
1080 (void) fprintf(stderr, "Couldn't create pipe: %s\n",
1081 strerror(errno));
1082 return (-1);
1085 if ((pid = vfork()) < 0) {
1086 (void) close(pfds[0]);
1087 (void) close(pfds[1]);
1088 (void) fprintf(stderr, "Couldn't fork child process: %s\n",
1089 strerror(errno));
1090 return (-1);
1091 } else if (pid == 0) {
1093 * Child.
1095 (void) close(pfds[0]);
1097 * Send stderr messages down the pipe so that we can detect
1098 * them in the parent process.
1100 if (pfds[1] != STDERR_FILENO) {
1101 (void) dup2(pfds[1], STDERR_FILENO);
1102 (void) close(pfds[1]);
1105 * This shell does not inherit the additional privilege
1106 * we have in our Permitted set.
1108 (void) execv(path, arglist);
1109 _exit(127);
1112 * Parent.
1114 istat = signal(SIGINT, SIG_IGN);
1115 qstat = signal(SIGQUIT, SIG_IGN);
1117 (void) close(pfds[1]);
1118 seen_stderr_traffic = B_FALSE;
1119 while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) {
1121 * If any data is read from the pipe the child process
1122 * has output something on stderr so we set the boolean
1123 * 'seen_stderr_traffic' to true, which will cause the
1124 * function to return -1.
1126 (void) write(STDERR_FILENO, buf, cnt);
1127 seen_stderr_traffic = B_TRUE;
1129 (void) close(pfds[0]);
1130 while ((w = wait(&status)) != pid && w != -1)
1132 if (w == -1)
1133 status = -1;
1135 (void) signal(SIGINT, istat);
1136 (void) signal(SIGQUIT, qstat);
1138 return (seen_stderr_traffic ? -1 : status);
1141 static void
1142 source(int argc, char *argv[])
1144 struct stat stb;
1145 static BUF buffer;
1146 BUF *bp;
1147 int x, readerr, f, amt;
1148 char *last, *name, buf[RCP_BUFSIZE];
1149 off_t off, size, i;
1150 ssize_t cnt;
1151 struct linger lingerbuf;
1153 for (x = 0; x < argc; x++) {
1154 name = argv[x];
1155 if ((f = open(name, O_RDONLY, 0)) < 0) {
1156 error("rcp: %s: %s\n", name, strerror(errno));
1157 continue;
1159 if (fstat(f, &stb) < 0)
1160 goto notreg;
1161 switch (stb.st_mode&S_IFMT) {
1163 case S_IFREG:
1164 break;
1166 case S_IFDIR:
1167 if (iamrecursive) {
1168 (void) close(f);
1169 rsource(name, &stb);
1170 continue;
1172 /* FALLTHROUGH */
1173 default:
1174 notreg:
1175 (void) close(f);
1176 error("rcp: %s: not a plain file\n", name);
1177 continue;
1179 last = rindex(name, '/');
1180 if (last == 0)
1181 last = name;
1182 else
1183 last++;
1184 if (pflag) {
1185 time_t mtime, atime;
1186 time_t now;
1189 * Make it compatible with possible future
1190 * versions expecting microseconds.
1192 mtime = stb.st_mtime;
1193 atime = stb.st_atime;
1195 if ((mtime < 0) || (atime < 0)) {
1196 now = time(NULL);
1198 if (mtime < 0) {
1199 mtime = now;
1200 error("negative modification time on "
1201 "%s; not preserving\n", name);
1203 if (atime < 0) {
1204 atime = now;
1205 error("negative access time on "
1206 "%s; not preserving\n", name);
1209 (void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n",
1210 mtime, atime);
1211 (void) desrcpwrite(rem, buf, strlen(buf));
1212 if (response() < 0) {
1213 (void) close(f);
1214 continue;
1217 (void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
1218 (uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size,
1219 last);
1220 (void) desrcpwrite(rem, buf, strlen(buf));
1221 if (response() < 0) {
1222 (void) close(f);
1223 continue;
1226 /* ACL support: send */
1227 if (aclflag | acl_aclflag) {
1228 /* get acl from f and send it over */
1229 if (sendacl(f) == ACL_FAIL) {
1230 (void) close(f);
1231 continue;
1234 if ((krb5auth_flag > 0) || (iamremote == 1)) {
1235 bp = allocbuf(&buffer, f, RCP_BUFSIZE);
1236 if (bp == NULLBUF) {
1237 (void) close(f);
1238 continue;
1240 readerr = 0;
1241 for (i = 0; i < stb.st_size; i += bp->cnt) {
1242 amt = bp->cnt;
1243 if (i + amt > stb.st_size)
1244 amt = stb.st_size - i;
1245 if (readerr == 0 &&
1246 read(f, bp->buf, amt) != amt)
1247 readerr = errno;
1248 (void) desrcpwrite(rem, bp->buf, amt);
1250 (void) close(f);
1251 if (readerr == 0)
1252 (void) desrcpwrite(rem, "", 1);
1253 else
1254 error("rcp: %s: %s\n", name,
1255 error_message(readerr));
1256 } else {
1257 cnt = off = 0;
1258 size = stb.st_size;
1259 while (size != 0) {
1260 amt = MIN(size, SENDFILE_SIZE);
1261 cnt = sendfile(rem, f, &off, amt);
1262 if (cnt == -1) {
1263 if (errno == EINTR) {
1264 continue;
1265 } else {
1266 break;
1269 if (cnt == 0)
1270 break;
1271 size -= cnt;
1273 if (cnt < 0) {
1274 error("rcp: %s: %s\n", name, strerror(errno));
1275 } else if (cnt == 0 && size != 0) {
1276 error("rcp: %s: unexpected end of file\n",
1277 name);
1278 lingerbuf.l_onoff = 1;
1279 lingerbuf.l_linger = 0;
1280 (void) setsockopt(rem, SOL_SOCKET, SO_LINGER,
1281 &lingerbuf, sizeof (lingerbuf));
1283 * When response() (see below) is invoked it
1284 * tries to read data from closed handle which
1285 * triggers error and lostconn() function.
1286 * lostconn() terminates the program with
1287 * appropriate message.
1289 (void) close(rem);
1290 rem = -1;
1291 } else {
1292 (void) write(rem, "", 1);
1294 (void) close(f);
1296 (void) response();
1301 static void
1302 rsource(char *name, struct stat *statp)
1304 DIR *d;
1305 struct dirent *dp;
1306 char *last, *vect[1];
1307 char path[MAXPATHLEN];
1309 if (!(d = opendir(name))) {
1310 error("rcp: %s: %s\n", name, strerror(errno));
1311 return;
1313 last = rindex(name, '/');
1314 if (last == 0)
1315 last = name;
1316 else
1317 last++;
1318 if (pflag) {
1319 (void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n",
1320 statp->st_mtime, statp->st_atime);
1321 (void) desrcpwrite(rem, path, strlen(path));
1322 if (response() < 0) {
1323 (void) closedir(d);
1324 return;
1327 (void) snprintf(path, sizeof (path), "D%04o %d %s\n",
1328 (uint_t)(statp->st_mode & 07777), 0, last);
1329 (void) desrcpwrite(rem, path, strlen(path));
1331 /* acl support for directory */
1332 if (aclflag) {
1333 /* get acl from f and send it over */
1334 if (sendacl(d->dd_fd) == ACL_FAIL) {
1335 (void) closedir(d);
1336 return;
1340 if (response() < 0) {
1341 (void) closedir(d);
1342 return;
1345 while (dp = readdir(d)) {
1346 if (dp->d_ino == 0)
1347 continue;
1348 if ((strcmp(dp->d_name, ".") == 0) ||
1349 (strcmp(dp->d_name, "..") == 0))
1350 continue;
1351 if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >=
1352 MAXPATHLEN - 1) {
1353 error("%s/%s: name too long.\n", name, dp->d_name);
1354 continue;
1356 (void) snprintf(path, sizeof (path), "%s/%s",
1357 name, dp->d_name);
1358 vect[0] = path;
1359 source(1, vect);
1361 (void) closedir(d);
1362 (void) desrcpwrite(rem, "E\n", 2);
1363 (void) response();
1366 static int
1367 response(void)
1369 register char *cp;
1370 char ch, resp, rbuf[RCP_BUFSIZE];
1372 if (desrcpread(rem, &resp, 1) != 1)
1373 lostconn();
1374 cp = rbuf;
1375 switch (resp) {
1376 case 0: /* ok */
1377 return (0);
1378 default:
1379 *cp++ = resp;
1380 /* FALLTHROUGH */
1381 case 1: /* error, followed by err msg */
1382 case 2: /* fatal error, "" */
1383 do {
1384 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1385 lostconn();
1386 *cp++ = ch;
1387 } while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n');
1389 if (!iamremote)
1390 (void) write(STDERR_FILENO, rbuf, cp - rbuf);
1391 ++errs;
1392 if (resp == 1)
1393 return (-1);
1394 exit(1);
1396 /*NOTREACHED*/
1399 static void
1400 lostconn(void)
1402 if (!iamremote)
1403 (void) fprintf(stderr, "rcp: lost connection\n");
1404 exit(1);
1408 static void
1409 sink(int argc, char *argv[])
1411 char *cp;
1412 static BUF buffer;
1413 struct stat stb;
1414 struct timeval tv[2];
1415 BUF *bp;
1416 off_t i, j;
1417 char ch, *targ, *why;
1418 int amt, count, exists, first, mask, mode;
1419 off_t size;
1420 int ofd, setimes, targisdir, wrerr;
1421 char *np, *vect[1], buf[RCP_BUFSIZE];
1422 char *namebuf = NULL;
1423 size_t namebuf_sz = 0;
1424 size_t need;
1426 #define atime tv[0]
1427 #define mtime tv[1]
1428 #define SCREWUP(str) { why = str; goto screwup; }
1430 setimes = targisdir = 0;
1431 mask = umask(0);
1432 if (!pflag)
1433 (void) umask(mask);
1434 if (argc != 1) {
1435 error("rcp: ambiguous target\n");
1436 exit(1);
1438 targ = *argv;
1439 if (targetshouldbedirectory)
1440 verifydir(targ);
1441 (void) desrcpwrite(rem, "", 1);
1443 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
1444 targisdir = 1;
1445 for (first = 1; ; first = 0) {
1446 cp = buf;
1447 if (desrcpread(rem, cp, 1) <= 0) {
1448 if (namebuf != NULL)
1449 free(namebuf);
1450 return;
1453 if (*cp++ == '\n')
1454 SCREWUP("unexpected <newline>");
1455 do {
1456 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1457 SCREWUP("lost connection");
1458 *cp++ = ch;
1459 } while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n');
1460 *cp = 0;
1462 if (buf[0] == '\01' || buf[0] == '\02') {
1463 if (iamremote == 0)
1464 (void) write(STDERR_FILENO, buf + 1,
1465 strlen(buf + 1));
1466 if (buf[0] == '\02')
1467 exit(1);
1468 errs++;
1469 continue;
1471 if (buf[0] == 'E') {
1472 (void) desrcpwrite(rem, "", 1);
1473 if (namebuf != NULL)
1474 free(namebuf);
1475 return;
1478 if (ch == '\n')
1479 *--cp = 0;
1480 cp = buf;
1481 if (*cp == 'T') {
1482 setimes++;
1483 cp++;
1484 mtime.tv_sec = strtol(cp, &cp, 0);
1485 if (*cp++ != ' ')
1486 SCREWUP("mtime.sec not delimited");
1487 mtime.tv_usec = strtol(cp, &cp, 0);
1488 if (*cp++ != ' ')
1489 SCREWUP("mtime.usec not delimited");
1490 atime.tv_sec = strtol(cp, &cp, 0);
1491 if (*cp++ != ' ')
1492 SCREWUP("atime.sec not delimited");
1493 atime.tv_usec = strtol(cp, &cp, 0);
1494 if (*cp++ != '\0')
1495 SCREWUP("atime.usec not delimited");
1496 (void) desrcpwrite(rem, "", 1);
1497 continue;
1499 if (*cp != 'C' && *cp != 'D') {
1501 * Check for the case "rcp remote:foo\* local:bar".
1502 * In this case, the line "No match." can be returned
1503 * by the shell before the rcp command on the remote is
1504 * executed so the ^Aerror_message convention isn't
1505 * followed.
1507 if (first) {
1508 error("%s\n", cp);
1509 exit(1);
1511 SCREWUP("expected control record")
1513 mode = 0;
1514 for (++cp; cp < buf + 5; cp++) {
1515 if (*cp < '0' || *cp > '7')
1516 SCREWUP("bad mode");
1517 mode = (mode << 3) | (*cp - '0');
1519 if (*cp++ != ' ')
1520 SCREWUP("mode not delimited");
1521 size = 0;
1522 while (isdigit(*cp))
1523 size = size * 10 + (*cp++ - '0');
1524 if (*cp++ != ' ')
1525 SCREWUP("size not delimited");
1526 if (targisdir) {
1527 need = strlen(targ) + sizeof ("/") + strlen(cp);
1528 if (need > namebuf_sz) {
1529 if ((namebuf = realloc(namebuf, need)) ==
1530 NULL) {
1531 error("rcp: out of memory\n");
1532 exit(1);
1534 namebuf_sz = need;
1536 (void) snprintf(namebuf, need, "%s%s%s", targ,
1537 *targ ? "/" : "", cp);
1538 np = namebuf;
1539 } else {
1540 np = targ;
1543 exists = stat(np, &stb) == 0;
1544 if (buf[0] == 'D') {
1545 if (exists) {
1546 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
1547 if (aclflag | acl_aclflag) {
1549 * consume acl in the pipe
1550 * fd = -1 to indicate the
1551 * special case
1553 if (recvacl(-1, exists, pflag)
1554 == ACL_FAIL) {
1555 goto bad;
1558 errno = ENOTDIR;
1559 goto bad;
1561 if (pflag)
1562 (void) chmod(np, mode);
1563 } else if (mkdir(np, mode) < 0) {
1564 if (aclflag) {
1565 /* consume acl in the pipe */
1566 (void) recvacl(-1, exists, pflag);
1568 goto bad;
1571 /* acl support for directories */
1572 if (aclflag | acl_aclflag) {
1573 int dfd;
1575 if ((dfd = open(np, O_RDONLY)) == -1)
1576 goto bad;
1578 /* get acl and set it to ofd */
1579 if (recvacl(dfd, exists, pflag) == ACL_FAIL) {
1580 (void) close(dfd);
1581 if (!exists)
1582 (void) rmdir(np);
1583 goto bad;
1585 (void) close(dfd);
1588 vect[0] = np;
1589 sink(1, vect);
1590 if (setimes) {
1591 setimes = 0;
1592 if (utimes(np, tv) < 0)
1593 error("rcp: can't set "
1594 "times on %s: %s\n",
1595 np, strerror(errno));
1597 continue;
1600 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
1601 bad:
1602 error("rcp: %s: %s\n", np, strerror(errno));
1603 continue;
1607 * If the output file exists we have to force zflag off
1608 * to avoid erroneously seeking past old data.
1610 zopen(ofd, zflag && !exists);
1612 if (exists && pflag)
1613 (void) fchmod(ofd, mode);
1615 (void) desrcpwrite(rem, "", 1);
1618 * ACL support: receiving
1620 if (aclflag | acl_aclflag) {
1621 /* get acl and set it to ofd */
1622 if (recvacl(ofd, exists, pflag) == ACL_FAIL) {
1623 (void) close(ofd);
1624 if (!exists)
1625 (void) unlink(np);
1626 continue;
1630 if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) {
1631 (void) close(ofd);
1632 continue;
1634 cp = bp->buf;
1635 count = 0;
1636 wrerr = 0;
1637 for (i = 0; i < size; i += RCP_BUFSIZE) {
1638 amt = RCP_BUFSIZE;
1639 if (i + amt > size)
1640 amt = size - i;
1641 count += amt;
1642 do {
1643 j = desrcpread(rem, cp, amt);
1644 if (j <= 0) {
1645 int sverrno = errno;
1648 * Connection to supplier lost.
1649 * Truncate file to correspond
1650 * to amount already transferred.
1652 * Note that we must call ftruncate()
1653 * before any call to error() (which
1654 * might result in a SIGPIPE and
1655 * sudden death before we have a chance
1656 * to correct the file's size).
1658 size = lseek(ofd, 0, SEEK_CUR);
1659 if ((ftruncate(ofd, size) == -1) &&
1660 (errno != EINVAL) &&
1661 (errno != EACCES))
1662 #define TRUNCERR "rcp: can't truncate %s: %s\n"
1663 error(TRUNCERR, np,
1664 strerror(errno));
1665 error("rcp: %s\n",
1666 j ? strerror(sverrno) :
1667 "dropped connection");
1668 (void) close(ofd);
1669 exit(1);
1671 amt -= j;
1672 cp += j;
1673 } while (amt > 0);
1674 if (count == bp->cnt) {
1675 cp = bp->buf;
1676 if (wrerr == 0 &&
1677 zwrite(ofd, cp, count) < 0)
1678 wrerr++;
1679 count = 0;
1682 if (count != 0 && wrerr == 0 &&
1683 zwrite(ofd, bp->buf, count) < 0)
1684 wrerr++;
1685 if (zclose(ofd) < 0)
1686 wrerr++;
1688 if ((ftruncate(ofd, size) == -1) && (errno != EINVAL) &&
1689 (errno != EACCES)) {
1690 error(TRUNCERR, np, strerror(errno));
1692 (void) close(ofd);
1693 (void) response();
1694 if (setimes) {
1695 setimes = 0;
1696 if (utimes(np, tv) < 0)
1697 error("rcp: can't set times on %s: %s\n",
1698 np, strerror(errno));
1700 if (wrerr)
1701 error("rcp: %s: %s\n", np, strerror(errno));
1702 else
1703 (void) desrcpwrite(rem, "", 1);
1705 screwup:
1706 error("rcp: protocol screwup: %s\n", why);
1707 exit(1);
1710 #ifndef roundup
1711 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
1712 #endif /* !roundup */
1714 static BUF *
1715 allocbuf(BUF *bp, int fd, int blksize)
1717 struct stat stb;
1718 int size;
1720 if (fstat(fd, &stb) < 0) {
1721 error("rcp: fstat: %s\n", strerror(errno));
1722 return (0);
1724 size = roundup(stb.st_blksize, blksize);
1725 if (size == 0)
1726 size = blksize;
1727 if (bp->cnt < size) {
1728 if (bp->buf != 0)
1729 free(bp->buf);
1730 bp->buf = (char *)malloc((uint_t)size);
1731 if (!bp->buf) {
1732 error("rcp: malloc: out of memory\n");
1733 return (0);
1736 bp->cnt = size;
1737 return (bp);
1740 static void
1741 usage(void)
1743 (void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"),
1744 gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
1745 #ifdef DEBUG
1746 "[-D port] "
1747 #endif /* DEBUG */
1748 "f1 f2; or:\n"),
1749 gettext("\trcp [-r] [-p] [-a] [-x] "
1750 #ifdef DEBUG
1751 "[-D port] "
1752 #endif /* DEBUG */
1753 "[-k realm] [-PN / -PO] f1...fn d2\n"));
1754 exit(1);
1759 * sparse file support
1762 static off_t zbsize;
1763 static off_t zlastseek;
1765 /* is it ok to try to create holes? */
1766 static void
1767 zopen(int fd, int flag)
1769 struct stat st;
1771 zbsize = 0;
1772 zlastseek = 0;
1774 if (flag &&
1775 fstat(fd, &st) == 0 &&
1776 (st.st_mode & S_IFMT) == S_IFREG)
1777 zbsize = st.st_blksize;
1780 /* write and/or seek */
1781 static int
1782 zwrite(int fd, char *buf, int nbytes)
1784 off_t block = zbsize ? zbsize : nbytes;
1786 do {
1787 if (block > nbytes)
1788 block = nbytes;
1789 nbytes -= block;
1791 if (!zbsize || notzero(buf, block)) {
1792 register int n, count = block;
1794 do {
1795 if ((n = write(fd, buf, count)) < 0)
1796 return (-1);
1797 buf += n;
1798 } while ((count -= n) > 0);
1799 zlastseek = 0;
1800 } else {
1801 if (lseek(fd, (off_t)block, SEEK_CUR) < 0)
1802 return (-1);
1803 buf += block;
1804 zlastseek = 1;
1806 } while (nbytes > 0);
1808 return (0);
1811 /* write last byte of file if necessary */
1812 static int
1813 zclose(int fd)
1815 zbsize = 0;
1817 if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 ||
1818 zwrite(fd, "", 1) < 0))
1819 return (-1);
1820 else
1821 return (0);
1824 /* return true if buffer is not all zeros */
1825 static int
1826 notzero(char *p, int n)
1828 register int result = 0;
1830 while ((int)p & 3 && --n >= 0)
1831 result |= *p++;
1833 while ((n -= 4 * sizeof (int)) >= 0) {
1834 /* LINTED */
1835 result |= ((int *)p)[0];
1836 /* LINTED */
1837 result |= ((int *)p)[1];
1838 /* LINTED */
1839 result |= ((int *)p)[2];
1840 /* LINTED */
1841 result |= ((int *)p)[3];
1842 if (result)
1843 return (result);
1844 p += 4 * sizeof (int);
1846 n += 4 * sizeof (int);
1848 while (--n >= 0)
1849 result |= *p++;
1851 return (result);
1855 * New functions to support ACLs
1859 * Get acl from f and send it over.
1860 * ACL record includes acl entry count, acl text length, and acl text.
1862 static int
1863 sendacl(int f)
1865 int aclcnt;
1866 char *acltext;
1867 char buf[BUFSIZ];
1868 acl_t *aclp;
1869 char acltype;
1870 int aclerror;
1871 int trivial;
1874 aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp);
1875 if (aclerror != 0) {
1876 error("can't retrieve ACL: %s \n", acl_strerror(aclerror));
1877 return (ACL_FAIL);
1881 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0
1882 * then don't do the malloc and facl(fd, getcntcmd,...);
1883 * since the remote side doesn't support alternate style ACL's.
1886 if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) {
1887 aclcnt = MIN_ACL_ENTRIES;
1888 acltype = 'A';
1889 trivial = ACL_IS_TRIVIAL;
1890 } else {
1892 aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0;
1894 if (aclp) {
1895 acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A';
1896 aclcnt = acl_cnt(aclp);
1897 trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL);
1898 } else {
1899 acltype = 'A';
1900 aclcnt = MIN_ACL_ENTRIES;
1901 trivial = ACL_IS_TRIVIAL;
1906 /* send the acl count over */
1907 (void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt);
1908 (void) desrcpwrite(rem, buf, strlen(buf));
1911 * only send acl when we have an aclp, which would
1912 * imply its not trivial.
1914 if (aclp && (trivial != ACL_IS_TRIVIAL)) {
1915 acltext = acl_totext(aclp, 0);
1916 if (acltext == NULL) {
1917 error("rcp: failed to convert to text\n");
1918 acl_free(aclp);
1919 return (ACL_FAIL);
1922 /* send ACLs over: send the length first */
1923 (void) snprintf(buf, sizeof (buf), "%c%d\n",
1924 acltype, strlen(acltext));
1926 (void) desrcpwrite(rem, buf, strlen(buf));
1927 (void) desrcpwrite(rem, acltext, strlen(acltext));
1928 free(acltext);
1929 if (response() < 0) {
1930 acl_free(aclp);
1931 return (ACL_FAIL);
1936 if (aclp)
1937 acl_free(aclp);
1938 return (ACL_OK);
1942 * Use this routine to get acl entry count and acl text size (in bytes)
1944 static int
1945 getaclinfo(int *cnt, int *acltype)
1947 char buf[BUFSIZ];
1948 char *cp;
1949 char ch;
1951 /* get acl count */
1952 cp = buf;
1953 if (desrcpread(rem, cp, 1) <= 0)
1954 return (ACL_FAIL);
1956 switch (*cp++) {
1957 case 'A':
1958 *acltype = 0;
1959 break;
1960 case 'Z':
1961 *acltype = 1;
1962 break;
1963 default:
1964 error("rcp: expect an ACL record, but got %c\n", *cp);
1965 return (ACL_FAIL);
1967 do {
1968 if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) {
1969 error("rcp: lost connection ..\n");
1970 return (ACL_FAIL);
1972 *cp++ = ch;
1973 } while (cp < &buf[BUFSIZ - 1] && ch != '\n');
1974 if (ch != '\n') {
1975 error("rcp: ACL record corrupted \n");
1976 return (ACL_FAIL);
1978 cp = &buf[1];
1979 *cnt = strtol(cp, &cp, 0);
1980 if (*cp != '\n') {
1981 error("rcp: ACL record corrupted \n");
1982 return (ACL_FAIL);
1984 return (ACL_OK);
1989 * Receive acl from the pipe and set it to f
1991 static int
1992 recvacl(int f, int exists, int preserve)
1994 int aclcnt; /* acl entry count */
1995 int aclsize; /* acl text length */
1996 int j;
1997 char *tp;
1998 char *acltext; /* external format */
1999 acl_t *aclp;
2000 int acltype;
2001 int min_entries;
2002 int aclerror;
2004 /* get acl count */
2005 if (getaclinfo(&aclcnt, &acltype) != ACL_OK)
2006 return (ACL_FAIL);
2008 if (acltype == 0) {
2009 min_entries = MIN_ACL_ENTRIES;
2010 } else {
2011 min_entries = 1;
2014 if (aclcnt > min_entries) {
2015 /* get acl text size */
2016 if (getaclinfo(&aclsize, &acltype) != ACL_OK)
2017 return (ACL_FAIL);
2018 if ((acltext = malloc(aclsize + 1)) == NULL) {
2019 error("rcp: cant allocate memory: %d\n", aclsize);
2020 return (ACL_FAIL);
2023 tp = acltext;
2024 do {
2025 j = desrcpread(rem, tp, aclsize);
2026 if (j <= 0) {
2027 error("rcp: %s\n", j ? strerror(errno) :
2028 "dropped connection");
2029 exit(1);
2031 aclsize -= j;
2032 tp += j;
2033 } while (aclsize > 0);
2034 *tp = '\0';
2036 if (preserve || !exists) {
2037 aclerror = acl_fromtext(acltext, &aclp);
2038 if (aclerror != 0) {
2039 error("rcp: failed to parse acl : %s\n",
2040 acl_strerror(aclerror));
2041 free(acltext);
2042 return (ACL_FAIL);
2045 if (f != -1) {
2046 if (facl_set(f, aclp) < 0) {
2047 error("rcp: failed to set acl\n");
2048 acl_free(aclp);
2049 free(acltext);
2050 return (ACL_FAIL);
2053 /* -1 means that just consume the data in the pipe */
2054 acl_free(aclp);
2056 free(acltext);
2057 (void) desrcpwrite(rem, "", 1);
2059 return (ACL_OK);
2063 static char *
2064 search_char(unsigned char *cp, unsigned char chr)
2066 int len;
2068 while (*cp) {
2069 if (*cp == chr)
2070 return ((char *)cp);
2071 if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
2072 len = 1;
2073 cp += len;
2075 return (0);
2079 static int
2080 desrcpread(int fd, char *buf, int len)
2082 return ((int)desread(fd, buf, len, 0));
2085 static int
2086 desrcpwrite(int fd, char *buf, int len)
2089 * Note that rcp depends on the same file descriptor being both
2090 * input and output to the remote side. This is bogus, especially
2091 * when rcp is being run by a rsh that pipes. Fix it here because
2092 * it would require significantly more work in other places.
2093 * --hartmans 1/96
2096 if (fd == 0)
2097 fd = 1;
2098 return ((int)deswrite(fd, buf, len, 0));
2101 static char **
2102 save_argv(int argc, char **argv)
2104 int i;
2106 char **local_argv = (char **)calloc((unsigned)argc + 1,
2107 (unsigned)sizeof (char *));
2110 * allocate an extra pointer, so that it is initialized to NULL and
2111 * execv() will work
2113 for (i = 0; i < argc; i++) {
2114 local_argv[i] = strsave(argv[i]);
2117 return (local_argv);
2120 #define SIZEOF_INADDR sizeof (struct in_addr)
2122 static void
2123 answer_auth(char *config_file, char *ccache_file)
2125 krb5_data pname_data, msg;
2126 krb5_creds creds, *new_creds;
2127 krb5_ccache cc;
2128 krb5_auth_context auth_context = NULL;
2130 if (config_file) {
2131 const char *filenames[2];
2133 filenames[1] = NULL;
2134 filenames[0] = config_file;
2135 if (krb5_set_config_files(bsd_context, filenames))
2136 exit(1);
2138 (void) memset((char *)&creds, 0, sizeof (creds));
2140 if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data))
2141 exit(1);
2143 if (krb5_read_message(bsd_context, (krb5_pointer) &rem,
2144 &creds.second_ticket))
2145 exit(1);
2147 if (ccache_file == NULL) {
2148 if (krb5_cc_default(bsd_context, &cc))
2149 exit(1);
2150 } else {
2151 if (krb5_cc_resolve(bsd_context, ccache_file, &cc))
2152 exit(1);
2155 if (krb5_cc_get_principal(bsd_context, cc, &creds.client))
2156 exit(1);
2158 if (krb5_parse_name(bsd_context, pname_data.data, &creds.server))
2159 exit(1);
2161 krb5_xfree(pname_data.data);
2162 if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds,
2163 &new_creds))
2164 exit(1);
2166 if (krb5_mk_req_extended(bsd_context, &auth_context,
2167 AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))
2168 exit(1);
2170 if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) {
2171 krb5_xfree(msg.data);
2172 exit(1);
2174 /* setup eblock for des_read and write */
2175 (void) krb5_copy_keyblock(bsd_context,
2176 &new_creds->keyblock, &session_key);
2178 /* OK process key */
2179 eblock.crypto_entry = session_key->enctype;
2180 eblock.key = (krb5_keyblock *)session_key;
2182 init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL,
2183 &desinbuf, &desoutbuf, CLIENT, &eblock);
2184 /* cleanup */
2185 krb5_free_cred_contents(bsd_context, &creds);
2186 krb5_free_creds(bsd_context, new_creds);
2187 krb5_xfree(msg.data);
2191 static void
2192 try_normal_rcp(int cur_argc, char **cur_argv)
2194 char *target;
2197 * Reset all KRB5 relevant flags and set the
2198 * cmd-buffer so that normal rcp works
2200 krb5auth_flag = encrypt_flag = encrypt_done = 0;
2201 cmd = cmd_orig;
2202 cmd_sunw = cmd_sunw_orig;
2204 if (cur_argc < 2)
2205 usage();
2207 if (cur_argc > 2)
2208 targetshouldbedirectory = 1;
2210 rem = -1;
2212 prev_argc = cur_argc;
2213 prev_argv = save_argv(cur_argc, cur_argv);
2215 (void) init_service(krb5auth_flag);
2217 if (target = colon(cur_argv[cur_argc - 1])) {
2218 toremote(target, cur_argc, cur_argv);
2219 } else {
2220 tolocal(cur_argc, cur_argv);
2221 if (targetshouldbedirectory)
2222 verifydir(cur_argv[cur_argc - 1]);
2224 exit(errs);
2225 /* NOTREACHED */
2229 static int
2230 init_service(int krb5flag)
2232 struct servent *sp;
2233 boolean_t success = B_FALSE;
2235 if (krb5flag > 0) {
2236 sp = getservbyname("kshell", "tcp");
2237 if (sp == NULL) {
2238 (void) fprintf(stderr,
2239 gettext("rcp: kshell/tcp: unknown service.\n"
2240 "trying normal shell/tcp service\n"));
2241 } else {
2242 portnumber = sp->s_port;
2243 success = B_TRUE;
2245 } else {
2246 portnumber = htons(IPPORT_CMDSERVER);
2247 success = B_TRUE;
2249 return (success);
2252 /*PRINTFLIKE1*/
2253 static void
2254 error(char *fmt, ...)
2256 va_list ap;
2257 char buf[RCP_BUFSIZE];
2258 char *cp = buf;
2260 va_start(ap, fmt);
2261 errs++;
2262 *cp++ = 1;
2263 (void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap);
2264 va_end(ap);
2266 (void) desrcpwrite(rem, buf, strlen(buf));
2267 if (iamremote == 0)
2268 (void) write(2, buf + 1, strlen(buf + 1));
2271 static void
2272 addargs(char **arglist, ...)
2274 va_list ap;
2275 int i = 0;
2276 char *pm;
2278 va_start(ap, arglist);
2279 while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL)
2280 if (strcmp(pm, ""))
2281 arglist[i++] = pm;
2282 arglist[i] = NULL;
2283 va_end(ap);