2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1983 The Regents of the University of California.
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
26 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
49 #include <priv_utils.h>
50 #include <sys/sendfile.h>
51 #include <sys/sysmacros.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"
64 #define RCP_BUFSIZE (64 * 1024)
66 #define RCP_ACL "/usr/lib/sunw,rcp"
67 /* see PSARC/1993/004/opinion */
74 static char *cmd_sunw
;
75 static struct passwd
*pwd
;
82 static int iamrecursive
;
83 static int targetshouldbedirectory
;
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
121 #include <profile/prof_int.h>
125 #define NULLBUF (BUF *) 0
126 #define MAXARGS 10 /* Number of arguments passed to execv() */
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 },
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 },
181 static char *rcmdproto
= NULL
;
182 static profile_option_strings rcmdversion
[] = {
183 { "rcmd_protocol", &rcmdproto
, 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
;
199 (void) setlocale(LC_ALL
, "");
201 if (strcmp(argv
[0], RCP_ACL
) == 0)
204 if (!(pwd
= getpwuid(userid
= getuid()))) {
205 (void) fprintf(stderr
, "rcp: unknown user %d.\n",
211 while ((ch
= getopt(argc
, argv
, "axdfprtz:D:k:P:ZK")) != EOF
) {
214 targetshouldbedirectory
= 1;
216 case 'f': /* "from" */
218 if (aclflag
| acl_aclflag
)
220 (void) desrcpwrite(rem
, "", 1);
222 case 'p': /* preserve access/mod times */
238 if (!krb5_privacy_allowed()) {
239 (void) fprintf(stderr
, gettext("rcp: "
240 "Encryption not supported.\n"));
248 if ((krb_realm
= (char *)strdup(optarg
)) == NULL
) {
249 (void) fprintf(stderr
, gettext("rcp:"
250 " Cannot malloc.\n"));
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 "
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 "
272 kcmd_proto
= KCMD_NEW_PROTOCOL
;
273 rcmdoption_done
= B_TRUE
;
284 portnumber
= htons(atoi(optarg
));
297 * if the user disables krb5 on the cmdline (-K), then skip
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
) {
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 */
313 * note that the call to profile_get_options_boolean
314 * with autologin_option can affect value of
317 (void) profile_get_options_boolean(bsd_context
->profile
,
323 if (krb5auth_flag
> 0) {
325 status
= krb5_init_context(&bsd_context
);
327 com_err("rcp", status
,
328 gettext("while initializing krb5"));
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
);
343 if (encrypt_flag
> 0)
344 (void) answer_auth(krb_config
, krb_cache
);
349 (void) setuid(userid
);
356 (void) setuid(userid
);
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");
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]);
376 com_err("rcp", status
,
377 gettext("while getting default realm"));
381 * See if encryption should be done for this realm
383 (void) profile_get_options_boolean(bsd_context
->profile
,
386 * Check the appdefaults section
388 (void) profile_get_options_boolean(bsd_context
->profile
,
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
) {
396 (void) fprintf(stderr
, gettext("rcp: Encryption"
397 " not supported.\n"));
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
;
408 (void) fprintf(stderr
, gettext("Unrecognized "
409 "KCMD protocol (%s)"), rcmdproto
);
416 targetshouldbedirectory
= 1;
420 if (portnumber
== 0) {
421 if (krb5auth_flag
> 0) {
422 retval
= init_service(krb5auth_flag
);
425 * Connecting to the kshell service failed,
426 * fallback to normal rcp & reset KRB5 flags.
428 krb5auth_flag
= encrypt_flag
= 0;
430 (void) init_service(krb5auth_flag
);
434 (void) init_service(krb5auth_flag
);
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"));
444 (void) fprintf(stderr
, gettext("[kcmd ver.2]\n"));
446 (void) fprintf(stderr
, gettext("Normal rcp session, port %d "
447 "in use.\n"), portnumber
);
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
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 "
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" : "",
486 targetshouldbedirectory
? " -d" : "");
488 (void) snprintf(cmd_sunw_orig
, cmdsiz
, "%s%s%s%s%s", RCP_ACL
,
489 iamrecursive
? " -r" : "",
492 targetshouldbedirectory
? " -d" : "");
495 prev_argv
= save_argv(argc
, argv
);
498 cmdsiz
= sizeof ("rcp -r -p -z -d");
499 if (((cmd
= (char *)malloc(cmdsiz
)) == NULL
)) {
500 (void) fprintf(stderr
, gettext("rcp: Cannot "
505 (void) snprintf(cmd
, cmdsiz
, "rcp%s%s%s%s",
506 iamrecursive
? " -r" : "",
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"));
518 (void) snprintf(cmd_sunw
, cmdsiz
, "%s%s%s%s%s", RCP_ACL
,
519 iamrecursive
? " -r" : "",
522 targetshouldbedirectory
? " -d" : "");
524 (void) signal(SIGPIPE
, (void (*)(int))lostconn
);
526 if (targ
= colon(argv
[argc
- 1]))
527 toremote(targ
, argc
, argv
);
530 if (targetshouldbedirectory
)
531 verifydir(argv
[argc
- 1]);
534 return (errs
> 0 ? EXIT_FAILURE
: EXIT_SUCCESS
);
539 toremote(char *targ
, int argc
, char *argv
[])
542 char *host
, *src
, *suser
, *thost
, *tuser
;
545 char bp
[RCP_BUFSIZE
];
547 char *arglist
[MAXARGS
+1];
548 buffersize
= RCP_BUFSIZE
;
554 if (thost
= search_char((unsigned char *)argv
[argc
- 1], '@')) {
556 tuser
= argv
[argc
- 1];
559 else if (!okname(tuser
))
562 thost
= argv
[argc
- 1];
565 thost
= removebrackets(thost
);
567 for (i
= 0; i
< argc
- 1; i
++) {
568 src
= colon(argv
[i
]);
569 if (src
) { /* remote to remote */
573 host
= search_char((unsigned char *)argv
[i
], '@');
576 host
= removebrackets(host
);
578 if (*suser
== '\0') {
579 suser
= pwd
->pw_name
;
580 } else if (!okname(suser
)) {
584 (void) snprintf(bp
, buffersize
, "'%s%s%s:%s'",
585 tuser
? tuser
: "", tuser
? "@" : "",
587 (void) addargs(arglist
, "rsh", host
, "-l",
588 suser
, "-n", cmd
, src
, bp
, (char *)NULL
);
590 host
= removebrackets(argv
[i
]);
591 (void) snprintf(bp
, buffersize
, "'%s%s%s:%s'",
592 tuser
? tuser
: "", tuser
? "@" : "",
594 (void) addargs(arglist
, "rsh", host
, "-n", cmd
,
595 src
, bp
, (char *)NULL
);
597 if (susystem(_PATH_RSH
, arglist
) == -1)
599 } else { /* local to remote */
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
,
620 0, /* No server seq # */
622 0, /* Not any port # */
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
));
640 (void) fprintf(stderr
,
641 gettext("rcp: kcmd to host "
643 "trying normal rcp...\n\n"),
645 error_message(status
));
647 (void) fprintf(stderr
,
648 gettext("trying normal"
652 * kcmd() failed, so we have to
653 * fallback to normal rcp
655 try_normal_rcp(prev_argc
, prev_argv
);
658 session_key
= &cred
->keyblock
;
659 if (kcmd_proto
== KCMD_NEW_PROTOCOL
) {
661 status
= krb5_auth_con_getlocalsubkey(bsd_context
, auth_context
, &session_key
);
663 com_err("rcp", status
,
677 eblock
.crypto_entry
=
678 session_key
->enctype
;
680 (krb5_keyblock
*)session_key
;
682 init_encrypt(encrypt_flag
,
683 bsd_context
, kcmd_proto
,
684 &desinbuf
, &desoutbuf
, CLIENT
,
686 if (encrypt_flag
> 0) {
687 char *s
= gettext("This rcp "
689 "encryption for all "
690 "data transmissions."
693 (void) write(2, s
, strlen(s
));
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.
711 * First see if the remote side will
712 * support both aclent_t and ace_t
715 (void) snprintf(bp
, buffersize
,
718 rem
= rcmd_af(&host
, portnumber
,
720 tuser
? tuser
: pwd
->pw_name
,
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
))
735 (void) snprintf(bp
, buffersize
,
736 "%s -t %s", cmd_sunw
, targ
);
740 rem
= rcmd_af(&host
, portnumber
,
756 * running non-acl rcp.
779 /* everything should be fine now */
780 (void) setuid(userid
);
790 tolocal(int argc
, char *argv
[])
793 char *host
, *src
, *suser
, *lhost
;
796 char bp
[RCP_BUFSIZE
];
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],
807 if (susystem(_PATH_CP
, arglist
) == -1)
814 host
= search_char((unsigned char *)argv
[i
], '@');
818 if (*suser
== '\0') {
819 suser
= pwd
->pw_name
;
820 } else if (!okname(suser
)) {
826 suser
= pwd
->pw_name
;
828 host
= removebrackets(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
,
845 0, /* No server seq # */
847 1, /* Not any port # */
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
));
863 (void) fprintf(stderr
,
865 "to host %s failed - %s,\n"
866 "trying normal rcp...\n\n"),
867 host
, error_message(status
));
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
);
879 session_key
= &cred
->keyblock
;
880 if (kcmd_proto
== KCMD_NEW_PROTOCOL
) {
881 status
= krb5_auth_con_getlocalsubkey(
882 bsd_context
, auth_context
,
885 com_err("rcp", status
,
887 "subkey for session");
892 "no subkey negotiated"
897 eblock
.crypto_entry
= session_key
->enctype
;
898 eblock
.key
= (krb5_keyblock
*)session_key
;
900 init_encrypt(encrypt_flag
, bsd_context
,
902 &desinbuf
, &desoutbuf
, CLIENT
,
904 if (encrypt_flag
> 0) {
905 char *s
= gettext("This rcp "
906 "session is using DES "
907 "encryption for all "
908 "data transmissions."
911 (void) write(2, s
, strlen(s
));
920 * ACL support: try to find out if the remote site is
921 * running acl cognizant version of rcp.
926 (void) snprintf(bp
, buffersize
, "%s -Zf %s", cmd_sunw
,
928 rem
= rcmd_af(&host
, portnumber
, pwd
->pw_name
, suser
,
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
))
948 * Try again without ace_acl support
951 (void) snprintf(bp
, buffersize
, "%s -f %s",
954 rem
= rcmd_af(&host
, portnumber
, pwd
->pw_name
,
955 suser
, bp
, 0, AF_INET6
);
963 sizeof (resp
)) != sizeof (resp
))
969 * The other side is running non-acl
970 * rcp. Try again with normal stuff.
973 (void) snprintf(bp
, buffersize
,
974 "%s -f %s", cmd
, src
);
977 rem
= rcmd_af(&host
, portnumber
,
978 pwd
->pw_name
, suser
, bp
, 0,
988 sink(1, argv
+ argc
- 1);
1001 if (stat(cp
, &stb
) >= 0) {
1002 if ((stb
.st_mode
& S_IFMT
) == S_IFDIR
)
1006 error("rcp: %s: %s.\n", cp
, strerror(errno
));
1013 boolean_t is_bracket_open
= B_FALSE
;
1017 is_bracket_open
= B_TRUE
;
1018 else if (*cp
== ']')
1019 is_bracket_open
= B_FALSE
;
1020 else if (*cp
== ':' && !is_bracket_open
)
1022 else if (*cp
== '/')
1031 register char *cp
= cp0
;
1038 if (!isalpha(c
) && !isdigit(c
) && c
!= '_' && c
!= '-')
1043 (void) fprintf(stderr
, "rcp: invalid user name %s\n", cp0
);
1049 removebrackets(char *str
)
1053 if ((str
[0] == '[') && (str
[strlen(str
) - 1] == ']')) {
1055 str
[strlen(str
) - 1] = '\0';
1061 susystem(char *path
, char **arglist
)
1064 register void (*istat
)(), (*qstat
)();
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",
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",
1091 } else if (pid
== 0) {
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
);
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)
1135 (void) signal(SIGINT
, istat
);
1136 (void) signal(SIGQUIT
, qstat
);
1138 return (seen_stderr_traffic
? -1 : status
);
1142 source(int argc
, char *argv
[])
1147 int x
, readerr
, f
, amt
;
1148 char *last
, *name
, buf
[RCP_BUFSIZE
];
1151 struct linger lingerbuf
;
1153 for (x
= 0; x
< argc
; x
++) {
1155 if ((f
= open(name
, O_RDONLY
, 0)) < 0) {
1156 error("rcp: %s: %s\n", name
, strerror(errno
));
1159 if (fstat(f
, &stb
) < 0)
1161 switch (stb
.st_mode
&S_IFMT
) {
1169 rsource(name
, &stb
);
1176 error("rcp: %s: not a plain file\n", name
);
1179 last
= rindex(name
, '/');
1185 time_t mtime
, atime
;
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)) {
1200 error("negative modification time on "
1201 "%s; not preserving\n", name
);
1205 error("negative access time on "
1206 "%s; not preserving\n", name
);
1209 (void) snprintf(buf
, sizeof (buf
), "T%ld 0 %ld 0\n",
1211 (void) desrcpwrite(rem
, buf
, strlen(buf
));
1212 if (response() < 0) {
1217 (void) snprintf(buf
, sizeof (buf
), "C%04o %lld %s\n",
1218 (uint_t
)(stb
.st_mode
& 07777), (longlong_t
)stb
.st_size
,
1220 (void) desrcpwrite(rem
, buf
, strlen(buf
));
1221 if (response() < 0) {
1226 /* ACL support: send */
1227 if (aclflag
| acl_aclflag
) {
1228 /* get acl from f and send it over */
1229 if (sendacl(f
) == ACL_FAIL
) {
1234 if ((krb5auth_flag
> 0) || (iamremote
== 1)) {
1235 bp
= allocbuf(&buffer
, f
, RCP_BUFSIZE
);
1236 if (bp
== NULLBUF
) {
1241 for (i
= 0; i
< stb
.st_size
; i
+= bp
->cnt
) {
1243 if (i
+ amt
> stb
.st_size
)
1244 amt
= stb
.st_size
- i
;
1246 read(f
, bp
->buf
, amt
) != amt
)
1248 (void) desrcpwrite(rem
, bp
->buf
, amt
);
1252 (void) desrcpwrite(rem
, "", 1);
1254 error("rcp: %s: %s\n", name
,
1255 error_message(readerr
));
1260 amt
= MIN(size
, SENDFILE_SIZE
);
1261 cnt
= sendfile(rem
, f
, &off
, amt
);
1263 if (errno
== EINTR
) {
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",
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.
1292 (void) write(rem
, "", 1);
1302 rsource(char *name
, struct stat
*statp
)
1306 char *last
, *vect
[1];
1307 char path
[MAXPATHLEN
];
1309 if (!(d
= opendir(name
))) {
1310 error("rcp: %s: %s\n", name
, strerror(errno
));
1313 last
= rindex(name
, '/');
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) {
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 */
1333 /* get acl from f and send it over */
1334 if (sendacl(d
->dd_fd
) == ACL_FAIL
) {
1340 if (response() < 0) {
1345 while (dp
= readdir(d
)) {
1348 if ((strcmp(dp
->d_name
, ".") == 0) ||
1349 (strcmp(dp
->d_name
, "..") == 0))
1351 if ((uint_t
)strlen(name
) + 1 + strlen(dp
->d_name
) >=
1353 error("%s/%s: name too long.\n", name
, dp
->d_name
);
1356 (void) snprintf(path
, sizeof (path
), "%s/%s",
1362 (void) desrcpwrite(rem
, "E\n", 2);
1370 char ch
, resp
, rbuf
[RCP_BUFSIZE
];
1372 if (desrcpread(rem
, &resp
, 1) != 1)
1381 case 1: /* error, followed by err msg */
1382 case 2: /* fatal error, "" */
1384 if (desrcpread(rem
, &ch
, sizeof (ch
)) != sizeof (ch
))
1387 } while (cp
< &rbuf
[RCP_BUFSIZE
] && ch
!= '\n');
1390 (void) write(STDERR_FILENO
, rbuf
, cp
- rbuf
);
1403 (void) fprintf(stderr
, "rcp: lost connection\n");
1409 sink(int argc
, char *argv
[])
1414 struct timeval tv
[2];
1417 char ch
, *targ
, *why
;
1418 int amt
, count
, exists
, first
, mask
, mode
;
1420 int ofd
, setimes
, targisdir
, wrerr
;
1421 char *np
, *vect
[1], buf
[RCP_BUFSIZE
];
1422 char *namebuf
= NULL
;
1423 size_t namebuf_sz
= 0;
1428 #define SCREWUP(str) { why = str; goto screwup; }
1430 setimes
= targisdir
= 0;
1435 error("rcp: ambiguous target\n");
1439 if (targetshouldbedirectory
)
1441 (void) desrcpwrite(rem
, "", 1);
1443 if (stat(targ
, &stb
) == 0 && (stb
.st_mode
& S_IFMT
) == S_IFDIR
)
1445 for (first
= 1; ; first
= 0) {
1447 if (desrcpread(rem
, cp
, 1) <= 0) {
1448 if (namebuf
!= NULL
)
1454 SCREWUP("unexpected <newline>");
1456 if (desrcpread(rem
, &ch
, sizeof (ch
)) != sizeof (ch
))
1457 SCREWUP("lost connection");
1459 } while (cp
< &buf
[RCP_BUFSIZE
- 1] && ch
!= '\n');
1462 if (buf
[0] == '\01' || buf
[0] == '\02') {
1464 (void) write(STDERR_FILENO
, buf
+ 1,
1466 if (buf
[0] == '\02')
1471 if (buf
[0] == 'E') {
1472 (void) desrcpwrite(rem
, "", 1);
1473 if (namebuf
!= NULL
)
1484 mtime
.tv_sec
= strtol(cp
, &cp
, 0);
1486 SCREWUP("mtime.sec not delimited");
1487 mtime
.tv_usec
= strtol(cp
, &cp
, 0);
1489 SCREWUP("mtime.usec not delimited");
1490 atime
.tv_sec
= strtol(cp
, &cp
, 0);
1492 SCREWUP("atime.sec not delimited");
1493 atime
.tv_usec
= strtol(cp
, &cp
, 0);
1495 SCREWUP("atime.usec not delimited");
1496 (void) desrcpwrite(rem
, "", 1);
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
1511 SCREWUP("expected control record")
1514 for (++cp
; cp
< buf
+ 5; cp
++) {
1515 if (*cp
< '0' || *cp
> '7')
1516 SCREWUP("bad mode");
1517 mode
= (mode
<< 3) | (*cp
- '0');
1520 SCREWUP("mode not delimited");
1522 while (isdigit(*cp
))
1523 size
= size
* 10 + (*cp
++ - '0');
1525 SCREWUP("size not delimited");
1527 need
= strlen(targ
) + sizeof ("/") + strlen(cp
);
1528 if (need
> namebuf_sz
) {
1529 if ((namebuf
= realloc(namebuf
, need
)) ==
1531 error("rcp: out of memory\n");
1536 (void) snprintf(namebuf
, need
, "%s%s%s", targ
,
1537 *targ
? "/" : "", cp
);
1543 exists
= stat(np
, &stb
) == 0;
1544 if (buf
[0] == 'D') {
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
1553 if (recvacl(-1, exists
, pflag
)
1562 (void) chmod(np
, mode
);
1563 } else if (mkdir(np
, mode
) < 0) {
1565 /* consume acl in the pipe */
1566 (void) recvacl(-1, exists
, pflag
);
1571 /* acl support for directories */
1572 if (aclflag
| acl_aclflag
) {
1575 if ((dfd
= open(np
, O_RDONLY
)) == -1)
1578 /* get acl and set it to ofd */
1579 if (recvacl(dfd
, exists
, pflag
) == ACL_FAIL
) {
1592 if (utimes(np
, tv
) < 0)
1593 error("rcp: can't set "
1594 "times on %s: %s\n",
1595 np
, strerror(errno
));
1600 if ((ofd
= open(np
, O_WRONLY
|O_CREAT
, mode
)) < 0) {
1602 error("rcp: %s: %s\n", np
, strerror(errno
));
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
) {
1630 if ((bp
= allocbuf(&buffer
, ofd
, RCP_BUFSIZE
)) == 0) {
1637 for (i
= 0; i
< size
; i
+= RCP_BUFSIZE
) {
1643 j
= desrcpread(rem
, cp
, amt
);
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
) &&
1662 #define TRUNCERR "rcp: can't truncate %s: %s\n"
1666 j
? strerror(sverrno
) :
1667 "dropped connection");
1674 if (count
== bp
->cnt
) {
1677 zwrite(ofd
, cp
, count
) < 0)
1682 if (count
!= 0 && wrerr
== 0 &&
1683 zwrite(ofd
, bp
->buf
, count
) < 0)
1685 if (zclose(ofd
) < 0)
1688 if ((ftruncate(ofd
, size
) == -1) && (errno
!= EINVAL
) &&
1689 (errno
!= EACCES
)) {
1690 error(TRUNCERR
, np
, strerror(errno
));
1696 if (utimes(np
, tv
) < 0)
1697 error("rcp: can't set times on %s: %s\n",
1698 np
, strerror(errno
));
1701 error("rcp: %s: %s\n", np
, strerror(errno
));
1703 (void) desrcpwrite(rem
, "", 1);
1706 error("rcp: protocol screwup: %s\n", why
);
1711 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
1712 #endif /* !roundup */
1715 allocbuf(BUF
*bp
, int fd
, int blksize
)
1720 if (fstat(fd
, &stb
) < 0) {
1721 error("rcp: fstat: %s\n", strerror(errno
));
1724 size
= roundup(stb
.st_blksize
, blksize
);
1727 if (bp
->cnt
< size
) {
1730 bp
->buf
= (char *)malloc((uint_t
)size
);
1732 error("rcp: malloc: out of memory\n");
1743 (void) fprintf(stderr
, "%s: \t%s\t%s", gettext("Usage"),
1744 gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
1749 gettext("\trcp [-r] [-p] [-a] [-x] "
1753 "[-k realm] [-PN / -PO] f1...fn d2\n"));
1759 * sparse file support
1762 static off_t zbsize
;
1763 static off_t zlastseek
;
1765 /* is it ok to try to create holes? */
1767 zopen(int fd
, int flag
)
1775 fstat(fd
, &st
) == 0 &&
1776 (st
.st_mode
& S_IFMT
) == S_IFREG
)
1777 zbsize
= st
.st_blksize
;
1780 /* write and/or seek */
1782 zwrite(int fd
, char *buf
, int nbytes
)
1784 off_t block
= zbsize
? zbsize
: nbytes
;
1791 if (!zbsize
|| notzero(buf
, block
)) {
1792 register int n
, count
= block
;
1795 if ((n
= write(fd
, buf
, count
)) < 0)
1798 } while ((count
-= n
) > 0);
1801 if (lseek(fd
, (off_t
)block
, SEEK_CUR
) < 0)
1806 } while (nbytes
> 0);
1811 /* write last byte of file if necessary */
1817 if (zlastseek
&& (lseek(fd
, (off_t
)-1, SEEK_CUR
) < 0 ||
1818 zwrite(fd
, "", 1) < 0))
1824 /* return true if buffer is not all zeros */
1826 notzero(char *p
, int n
)
1828 register int result
= 0;
1830 while ((int)p
& 3 && --n
>= 0)
1833 while ((n
-= 4 * sizeof (int)) >= 0) {
1835 result
|= ((int *)p
)[0];
1837 result
|= ((int *)p
)[1];
1839 result
|= ((int *)p
)[2];
1841 result
|= ((int *)p
)[3];
1844 p
+= 4 * sizeof (int);
1846 n
+= 4 * sizeof (int);
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.
1874 aclerror
= facl_get(f
, ACL_NO_TRIVIAL
, &aclp
);
1875 if (aclerror
!= 0) {
1876 error("can't retrieve ACL: %s \n", acl_strerror(aclerror
));
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
;
1889 trivial
= ACL_IS_TRIVIAL
;
1892 aclcnt
= (aclp
!= NULL
) ? acl_cnt(aclp
) : 0;
1895 acltype
= (acl_type(aclp
) != ACLENT_T
) ? 'Z' : 'A';
1896 aclcnt
= acl_cnt(aclp
);
1897 trivial
= (acl_flags(aclp
) & ACL_IS_TRIVIAL
);
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");
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
));
1929 if (response() < 0) {
1942 * Use this routine to get acl entry count and acl text size (in bytes)
1945 getaclinfo(int *cnt
, int *acltype
)
1953 if (desrcpread(rem
, cp
, 1) <= 0)
1964 error("rcp: expect an ACL record, but got %c\n", *cp
);
1968 if (desrcpread(rem
, &ch
, sizeof (ch
)) != sizeof (ch
)) {
1969 error("rcp: lost connection ..\n");
1973 } while (cp
< &buf
[BUFSIZ
- 1] && ch
!= '\n');
1975 error("rcp: ACL record corrupted \n");
1979 *cnt
= strtol(cp
, &cp
, 0);
1981 error("rcp: ACL record corrupted \n");
1989 * Receive acl from the pipe and set it to f
1992 recvacl(int f
, int exists
, int preserve
)
1994 int aclcnt
; /* acl entry count */
1995 int aclsize
; /* acl text length */
1998 char *acltext
; /* external format */
2005 if (getaclinfo(&aclcnt
, &acltype
) != ACL_OK
)
2009 min_entries
= MIN_ACL_ENTRIES
;
2014 if (aclcnt
> min_entries
) {
2015 /* get acl text size */
2016 if (getaclinfo(&aclsize
, &acltype
) != ACL_OK
)
2018 if ((acltext
= malloc(aclsize
+ 1)) == NULL
) {
2019 error("rcp: cant allocate memory: %d\n", aclsize
);
2025 j
= desrcpread(rem
, tp
, aclsize
);
2027 error("rcp: %s\n", j
? strerror(errno
) :
2028 "dropped connection");
2033 } while (aclsize
> 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
));
2046 if (facl_set(f
, aclp
) < 0) {
2047 error("rcp: failed to set acl\n");
2053 /* -1 means that just consume the data in the pipe */
2057 (void) desrcpwrite(rem
, "", 1);
2064 search_char(unsigned char *cp
, unsigned char chr
)
2070 return ((char *)cp
);
2071 if ((len
= mblen((char *)cp
, MB_CUR_MAX
)) <= 0)
2080 desrcpread(int fd
, char *buf
, int len
)
2082 return ((int)desread(fd
, buf
, len
, 0));
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.
2098 return ((int)deswrite(fd
, buf
, len
, 0));
2102 save_argv(int argc
, char **argv
)
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
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)
2123 answer_auth(char *config_file
, char *ccache_file
)
2125 krb5_data pname_data
, msg
;
2126 krb5_creds creds
, *new_creds
;
2128 krb5_auth_context auth_context
= NULL
;
2131 const char *filenames
[2];
2133 filenames
[1] = NULL
;
2134 filenames
[0] = config_file
;
2135 if (krb5_set_config_files(bsd_context
, filenames
))
2138 (void) memset((char *)&creds
, 0, sizeof (creds
));
2140 if (krb5_read_message(bsd_context
, (krb5_pointer
) &rem
, &pname_data
))
2143 if (krb5_read_message(bsd_context
, (krb5_pointer
) &rem
,
2144 &creds
.second_ticket
))
2147 if (ccache_file
== NULL
) {
2148 if (krb5_cc_default(bsd_context
, &cc
))
2151 if (krb5_cc_resolve(bsd_context
, ccache_file
, &cc
))
2155 if (krb5_cc_get_principal(bsd_context
, cc
, &creds
.client
))
2158 if (krb5_parse_name(bsd_context
, pname_data
.data
, &creds
.server
))
2161 krb5_xfree(pname_data
.data
);
2162 if (krb5_get_credentials(bsd_context
, KRB5_GC_USER_USER
, cc
, &creds
,
2166 if (krb5_mk_req_extended(bsd_context
, &auth_context
,
2167 AP_OPTS_USE_SESSION_KEY
, NULL
, new_creds
, &msg
))
2170 if (krb5_write_message(bsd_context
, (krb5_pointer
) & rem
, &msg
)) {
2171 krb5_xfree(msg
.data
);
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
);
2185 krb5_free_cred_contents(bsd_context
, &creds
);
2186 krb5_free_creds(bsd_context
, new_creds
);
2187 krb5_xfree(msg
.data
);
2192 try_normal_rcp(int cur_argc
, char **cur_argv
)
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;
2202 cmd_sunw
= cmd_sunw_orig
;
2208 targetshouldbedirectory
= 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
);
2220 tolocal(cur_argc
, cur_argv
);
2221 if (targetshouldbedirectory
)
2222 verifydir(cur_argv
[cur_argc
- 1]);
2230 init_service(int krb5flag
)
2233 boolean_t success
= B_FALSE
;
2236 sp
= getservbyname("kshell", "tcp");
2238 (void) fprintf(stderr
,
2239 gettext("rcp: kshell/tcp: unknown service.\n"
2240 "trying normal shell/tcp service\n"));
2242 portnumber
= sp
->s_port
;
2246 portnumber
= htons(IPPORT_CMDSERVER
);
2254 error(char *fmt
, ...)
2257 char buf
[RCP_BUFSIZE
];
2263 (void) vsnprintf(cp
, sizeof (buf
) - 1, fmt
, ap
);
2266 (void) desrcpwrite(rem
, buf
, strlen(buf
));
2268 (void) write(2, buf
+ 1, strlen(buf
+ 1));
2272 addargs(char **arglist
, ...)
2278 va_start(ap
, arglist
);
2279 while (i
< MAXARGS
&& (pm
= va_arg(ap
, char *)) != NULL
)