xref: /titanic_50/usr/src/cmd/cmd-inet/usr.bin/rcp.c (revision 0e42dee69ed771bf604dd1789fca9d77b5bbe302)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright (c) 1983 The Regents of the University of California.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms are permitted
13  * provided that the above copyright notice and this paragraph are
14  * duplicated in all such forms and that any documentation,
15  * advertising materials, and other materials related to such
16  * distribution and use acknowledge that the software was developed
17  * by the University of California, Berkeley.  The name of the
18  * University may not be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  */
22 
23 #define	_FILE_OFFSET_BITS  64
24 
25 /*
26  * rcp
27  */
28 #include <sys/param.h>
29 #include <sys/file.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/acl.h>
35 #include <dirent.h>
36 #include <signal.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <pwd.h>
40 #include <netdb.h>
41 #include <wchar.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <locale.h>
45 #include <strings.h>
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <limits.h>
51 #include <priv_utils.h>
52 #include <sys/sendfile.h>
53 #include <sys/sysmacros.h>
54 #include <sys/wait.h>
55 #include <aclutils.h>
56 
57 /*
58  * It seems like Berkeley got these from pathnames.h?
59  */
60 #define	_PATH_RSH	"/usr/bin/rsh"
61 #define	_PATH_CP	"/usr/bin/cp"
62 #define	_PATH_BSHELL	"/usr/bin/sh"
63 
64 #define	ACL_FAIL	1
65 #define	ACL_OK		0
66 #define	RCP_BUFSIZE	(64 * 1024)
67 
68 #define	RCP_ACL	"/usr/lib/sunw,rcp"
69 		/* see PSARC/1993/004/opinion */
70 
71 typedef struct _buf {
72 	int	cnt;
73 	char	*buf;
74 } BUF;
75 
76 static char *cmd_sunw;
77 static struct passwd *pwd;
78 static int errs;
79 static int pflag;
80 static uid_t userid;
81 static int rem;
82 static int zflag;
83 static int iamremote;
84 static int iamrecursive;
85 static int targetshouldbedirectory;
86 static int aclflag;
87 static int acl_aclflag;
88 static int retval = 0;
89 static int portnumber = 0;
90 
91 static void lostconn(void);
92 static char *search_char(unsigned char *, unsigned char);
93 static char *removebrackets(char *);
94 static char *colon(char *);
95 static int response(void);
96 static void usage(void);
97 static void source(int, char **);
98 static void sink(int, char **);
99 static void toremote(char *, int, char **);
100 static void tolocal(int, char **);
101 static void verifydir(char *);
102 static int okname(char *);
103 static int susystem(char *);
104 static void rsource(char *, struct stat *);
105 static int sendacl(int);
106 static int recvacl(int, int, int);
107 static int zwrite(int, char *, int);
108 static void zopen(int, int);
109 static int zclose(int);
110 static int notzero(char *, int);
111 static BUF *allocbuf(BUF *, int, int);
112 static void error(char *fmt, ...);
113 
114 /*
115  * As a 32 bit application, we can only transfer (2gb - 1) i.e 0x7FFFFFFF
116  * bytes of data. We would like the size to be aligned to the nearest
117  * MAXBOFFSET (8192) boundary for optimal performance.
118  */
119 #define	SENDFILE_SIZE	0x7FFFE000
120 
121 #include <k5-int.h>
122 #include <profile/prof_int.h>
123 #include <com_err.h>
124 #include <kcmd.h>
125 
126 #define	NULLBUF	(BUF *) 0
127 
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 */
137 
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;
142 static krb5_auth_context auth_context;
143 static krb5_flags authopts;
144 static krb5_error_code status;
145 
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);
152 
153 /*
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.
156  */
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 *);
161 
162 static int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
163 static int encrypt_flag = 0;	/* Flag set, when encryption is enabled */
164 static int encrypt_done = 0;	/* Flag set, if "-x" is specified */
165 static enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
166 
167 /* Flag set, if -PN / -PO is specified */
168 static boolean_t rcmdoption_done = B_FALSE;
169 
170 static profile_options_boolean option[] = {
171 	{ "encrypt", &encrypt_flag, 0 },
172 	{ NULL, NULL, 0 }
173 };
174 
175 static char *rcmdproto = NULL;
176 static profile_option_strings rcmdversion[] = {
177 	{ "rcmd_protocol", &rcmdproto, 0 },
178 	{ NULL, NULL, 0 }
179 };
180 
181 static char *realmdef[] = { "realms", NULL, "rcp", NULL };
182 static char *appdef[] = { "appdefaults", "rcp", NULL };
183 static char **prev_argv;
184 static int prev_argc;
185 
186 int
187 main(int argc, char *argv[])
188 {
189 	int ch, fflag, tflag;
190 	char *targ;
191 	size_t cmdsiz;
192 
193 	(void) setlocale(LC_ALL, "");
194 
195 	if (strcmp(argv[0], RCP_ACL) == 0)
196 		aclflag = 1;
197 
198 	if (!(pwd = getpwuid(userid = getuid()))) {
199 		(void) fprintf(stderr, "rcp: unknown user %d.\n",
200 		    (uint_t)userid);
201 		return (1);
202 	}
203 
204 	fflag = tflag = 0;
205 	while ((ch = getopt(argc, argv, "axdfprtz:D:k:P:Z")) != EOF) {
206 		switch (ch) {
207 		case 'd':
208 			targetshouldbedirectory = 1;
209 			break;
210 		case 'f':			/* "from" */
211 			fflag = 1;
212 			if (aclflag | acl_aclflag)
213 				/* ok response */
214 				(void) desrcpwrite(rem, "", 1);
215 			break;
216 		case 'p':			/* preserve access/mod times */
217 			++pflag;
218 			break;
219 		case 'r':
220 			++iamrecursive;
221 			break;
222 		case 't':			/* "to" */
223 			tflag = 1;
224 			break;
225 		case 'Z':
226 			acl_aclflag++;
227 			break;
228 		case 'x':
229 			if (!krb5_privacy_allowed()) {
230 				(void) fprintf(stderr, gettext("rcp: "
231 					"Encryption not supported.\n"));
232 				return (1);
233 			}
234 			encrypt_flag++;
235 			krb5auth_flag++;
236 			encrypt_done++;
237 			break;
238 		case 'k':
239 			if ((krb_realm = (char *)strdup(optarg)) == NULL) {
240 				(void) fprintf(stderr, gettext("rcp:"
241 					" Cannot malloc.\n"));
242 				return (1);
243 			}
244 			krb5auth_flag++;
245 			break;
246 		case 'P':
247 			if (strncmp(optarg, "O", 1) == 0) {
248 				if (rcmdoption_done == B_TRUE) {
249 					(void) fprintf(stderr, gettext("rcp: "
250 						"Only one of -PN and -PO "
251 						"allowed.\n"));
252 					usage();
253 				}
254 				kcmd_proto = KCMD_OLD_PROTOCOL;
255 				rcmdoption_done = B_TRUE;
256 			} else if (strncmp(optarg, "N", 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();
262 				}
263 				kcmd_proto = KCMD_NEW_PROTOCOL;
264 				rcmdoption_done = B_TRUE;
265 			} else {
266 				usage();
267 			}
268 			krb5auth_flag++;
269 			break;
270 		case 'a':
271 			krb5auth_flag++;
272 			break;
273 #ifdef DEBUG
274 		case 'D':
275 			portnumber = htons(atoi(optarg));
276 			krb5auth_flag++;
277 			break;
278 #endif /* DEBUG */
279 		case '?':
280 		default:
281 			usage();
282 		}
283 	}
284 	argc -= optind;
285 	argv += optind;
286 
287 	if (krb5auth_flag > 0) {
288 		status = krb5_init_context(&bsd_context);
289 		if (status) {
290 			com_err("rcp", status,
291 				gettext("while initializing krb5"));
292 			return (1);
293 		}
294 
295 		/*
296 		 * Set up buffers for desread and deswrite.
297 		 */
298 		desinbuf.data = des_inbuf;
299 		desoutbuf.data = des_outbuf;
300 		desinbuf.length = sizeof (des_inbuf);
301 		desoutbuf.length = sizeof (des_outbuf);
302 	}
303 
304 	if (fflag || tflag)
305 		if (encrypt_flag > 0)
306 			(void) answer_auth(krb_config, krb_cache);
307 
308 	if (fflag) {
309 		iamremote = 1;
310 		(void) response();
311 		(void) setuid(userid);
312 		source(argc, argv);
313 		return (errs);
314 	}
315 
316 	if (tflag) {
317 		iamremote = 1;
318 		(void) setuid(userid);
319 		sink(argc, argv);
320 		return (errs);
321 	}
322 
323 	if (argc < 2)
324 		usage();
325 
326 	/* This will make "rcmd_af()" magically get the proper privilege */
327 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL) == -1) {
328 		(void) fprintf(stderr, "rcp: must be set-uid root\n");
329 		exit(1);
330 	}
331 
332 	if (krb5auth_flag > 0) {
333 		/*
334 		 * Get our local realm to look up local realm options.
335 		 */
336 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
337 		if (status) {
338 			com_err("rcp", status,
339 				gettext("while getting default realm"));
340 			return (1);
341 		}
342 		/*
343 		 * See if encryption should be done for this realm
344 		 */
345 		profile_get_options_boolean(bsd_context->profile, realmdef,
346 						option);
347 		/*
348 		 * Check the appdefaults section
349 		 */
350 		profile_get_options_boolean(bsd_context->profile, appdef,
351 						option);
352 		profile_get_options_string(bsd_context->profile, appdef,
353 						rcmdversion);
354 		if ((encrypt_done > 0) || (encrypt_flag > 0)) {
355 			if (krb5_privacy_allowed() == TRUE) {
356 				encrypt_flag++;
357 			} else {
358 				(void) fprintf(stderr, gettext("rcp: Encryption"
359 							" not supported.\n"));
360 				return (1);
361 			}
362 		}
363 
364 		if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
365 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
366 				kcmd_proto = KCMD_NEW_PROTOCOL;
367 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
368 				kcmd_proto = KCMD_OLD_PROTOCOL;
369 			} else {
370 				(void) fprintf(stderr, gettext("Unrecognized "
371 					"KCMD protocol (%s)"), rcmdproto);
372 				return (1);
373 			}
374 		}
375 	}
376 
377 	if (argc > 2)
378 		targetshouldbedirectory = 1;
379 
380 	rem = -1;
381 
382 	if (portnumber == 0) {
383 		if (krb5auth_flag > 0) {
384 			retval = init_service(krb5auth_flag);
385 			if (!retval) {
386 				/*
387 				 * Connecting to the kshell service failed,
388 				 * fallback to normal rcp & reset KRB5 flags.
389 				 */
390 				krb5auth_flag = encrypt_flag = 0;
391 				encrypt_done = 0;
392 				(void) init_service(krb5auth_flag);
393 			}
394 		}
395 		else
396 			(void) init_service(krb5auth_flag);
397 	}
398 
399 #ifdef DEBUG
400 	if (retval || krb5auth_flag) {
401 		(void) fprintf(stderr, gettext("Kerberized rcp session, "
402 				"port %d in use "), portnumber);
403 		if (kcmd_proto == KCMD_OLD_PROTOCOL)
404 			(void) fprintf(stderr, gettext("[kcmd ver.1]\n"));
405 		else
406 			(void) fprintf(stderr, gettext("[kcmd ver.2]\n"));
407 	} else {
408 		(void) fprintf(stderr, gettext("Normal rcp session, port %d "
409 				"in use.\n"), portnumber);
410 	}
411 #endif /* DEBUG */
412 
413 	if (krb5auth_flag > 0) {
414 		/*
415 		 * We calculate here a buffer size that can be used in the
416 		 * allocation of the three buffers cmd, cmd_orig and
417 		 * cmd_sunw_orig that are used to hold different incantations
418 		 * of rcp.
419 		 */
420 		cmdsiz = MAX(sizeof ("-x rcp  -r -p -d -k ") +
421 		    strlen(krb_realm != NULL ? krb_realm : ""),
422 		    sizeof (RCP_ACL " -r -p -z -d"));
423 
424 		if (((cmd = (char *)malloc(cmdsiz)) == NULL) ||
425 			((cmd_sunw_orig = (char *)malloc(cmdsiz)) == NULL) ||
426 			((cmd_orig = (char *)malloc(cmdsiz)) == NULL)) {
427 			(void) fprintf(stderr, gettext("rcp: Cannot "
428 					"malloc.\n"));
429 			return (1);
430 		}
431 
432 		(void) snprintf(cmd, cmdsiz, "%srcp %s%s%s%s%s",
433 			encrypt_flag ? "-x " : "",
434 
435 			iamrecursive ? " -r" : "", pflag ? " -p" : "",
436 			targetshouldbedirectory ? " -d" : "",
437 			krb_realm != NULL ? " -k " : "",
438 			krb_realm != NULL ? krb_realm : "");
439 
440 		/*
441 		 * We would use cmd-orig as the 'cmd-buffer' if kerberized
442 		 * rcp fails, in which case we fallback to normal rcp. We also
443 		 * save argc & argv for the same purpose
444 		 */
445 		(void) snprintf(cmd_orig, cmdsiz, "rcp%s%s%s%s",
446 			iamrecursive ? " -r" : "",
447 			pflag ? " -p" : "",
448 			zflag ? " -z" : "",
449 			targetshouldbedirectory ? " -d" : "");
450 
451 		(void) snprintf(cmd_sunw_orig, cmdsiz, "%s%s%s%s%s", RCP_ACL,
452 			iamrecursive ? " -r" : "",
453 			pflag ? " -p" : "",
454 			zflag ? " -z" : "",
455 			targetshouldbedirectory ? " -d" : "");
456 
457 		prev_argc = argc;
458 		prev_argv = save_argv(argc, argv);
459 
460 	} else {
461 		cmdsiz = sizeof ("rcp -r -p -z -d");
462 		if (((cmd = (char *)malloc(cmdsiz)) == NULL)) {
463 			(void) fprintf(stderr, gettext("rcp: Cannot "
464 					"malloc.\n"));
465 			return (1);
466 		}
467 
468 		(void) snprintf(cmd, cmdsiz, "rcp%s%s%s%s",
469 			iamrecursive ? " -r" : "",
470 			pflag ? " -p" : "",
471 			zflag ? " -z" : "",
472 			targetshouldbedirectory ? " -d" : "");
473 	}
474 
475 	cmdsiz = sizeof (RCP_ACL " -r -p -z -d");
476 	if ((cmd_sunw = (char *)malloc(cmdsiz)) == NULL) {
477 		(void) fprintf(stderr, gettext("rcp: Cannot malloc.\n"));
478 		return (1);
479 	}
480 
481 	(void) snprintf(cmd_sunw, cmdsiz, "%s%s%s%s%s", RCP_ACL,
482 	    iamrecursive ? " -r" : "",
483 	    pflag ? " -p" : "",
484 	    zflag ? " -z" : "",
485 	    targetshouldbedirectory ? " -d" : "");
486 
487 	(void) signal(SIGPIPE, (void (*)(int))lostconn);
488 
489 	if (targ = colon(argv[argc - 1]))
490 		toremote(targ, argc, argv);
491 	else {
492 		tolocal(argc, argv);
493 		if (targetshouldbedirectory)
494 			verifydir(argv[argc - 1]);
495 	}
496 
497 	return (errs > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
498 }
499 
500 
501 static void
502 toremote(char *targ, int argc, char *argv[])
503 {
504 	int i;
505 	char *host, *src, *suser, *thost, *tuser;
506 	char resp;
507 	size_t buffersize;
508 	char bp[RCP_BUFSIZE];
509 	krb5_creds *cred;
510 	buffersize = RCP_BUFSIZE;
511 
512 	*targ++ = 0;
513 	if (*targ == 0)
514 		targ = ".";
515 
516 	if (thost = search_char((unsigned char *)argv[argc - 1], '@')) {
517 		*thost++ = 0;
518 		tuser = argv[argc - 1];
519 		if (*tuser == '\0')
520 			tuser = NULL;
521 		else if (!okname(tuser))
522 			exit(1);
523 	} else {
524 		thost = argv[argc - 1];
525 		tuser = NULL;
526 	}
527 	thost = removebrackets(thost);
528 
529 	for (i = 0; i < argc - 1; i++) {
530 		src = colon(argv[i]);
531 		if (src) {			/* remote to remote */
532 			*src++ = 0;
533 			if (*src == 0)
534 				src = ".";
535 			host = search_char((unsigned char *)argv[i], '@');
536 			if (host) {
537 				*host++ = 0;
538 				host = removebrackets(host);
539 				suser = argv[i];
540 				if (*suser == '\0') {
541 					suser = pwd->pw_name;
542 				} else if (!okname(suser)) {
543 					errs++;
544 					continue;
545 				}
546 				(void) snprintf(bp, buffersize,
547 				    "%s %s -l %s -n %s %s '%s%s%s:%s'",
548 				    _PATH_RSH, host, suser, cmd, src,
549 				    tuser ? tuser : "", tuser ? "@" : "",
550 				    thost, targ);
551 			} else {
552 				host = removebrackets(argv[i]);
553 				(void) snprintf(bp, buffersize,
554 					"%s %s -n %s %s '%s%s%s:%s'",
555 					_PATH_RSH, host, cmd, src,
556 					tuser ? tuser : "", tuser ? "@" : "",
557 					thost, targ);
558 			}
559 			if (susystem(bp) == -1)
560 				errs++;
561 		} else {			/* local to remote */
562 			if (rem == -1) {
563 				host = thost;
564 				if (krb5auth_flag > 0) {
565 
566 				(void) snprintf(bp, buffersize,
567 						"%s -t %s", cmd, targ);
568 				authopts = AP_OPTS_MUTUAL_REQUIRED;
569 				status = kcmd(&sock, &host,
570 					    portnumber,
571 					    pwd->pw_name,
572 					    tuser ? tuser :
573 					    pwd->pw_name,
574 					    bp,
575 					    0,
576 					    "host",
577 					    krb_realm,
578 					    bsd_context,
579 					    &auth_context,
580 					    &cred,
581 					    0,	/* No seq # */
582 					    0,	/* No server seq # */
583 					    authopts,
584 					    0,	/* Not any port # */
585 					    &kcmd_proto);
586 				if (status) {
587 					/*
588 					 * If new protocol requested, we dont
589 					 * fallback to less secure ones.
590 					 */
591 
592 					if (kcmd_proto == KCMD_NEW_PROTOCOL) {
593 						(void) fprintf(stderr,
594 							gettext("rcp: kcmdv2 "
595 							"to host %s failed - %s"
596 							"\nFallback to normal "
597 							"rcp denied."), host,
598 							error_message(status));
599 						exit(1);
600 					}
601 					if (status != -1) {
602 						(void) fprintf(stderr,
603 						gettext("rcp: kcmd to host "
604 						"%s failed - %s,\n"
605 						"trying normal rcp...\n\n"),
606 						host, error_message(status));
607 					} else {
608 						(void) fprintf(stderr,
609 							gettext("trying normal"
610 							" rcp...\n"));
611 					}
612 					/*
613 					 * kcmd() failed, so we have to
614 					 * fallback to normal rcp
615 					 */
616 					try_normal_rcp(prev_argc, prev_argv);
617 				} else {
618 					rem = sock;
619 					session_key = &cred->keyblock;
620 					if (kcmd_proto == KCMD_NEW_PROTOCOL) {
621 						/* CSTYLED */
622 						status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key);
623 						if (status) {
624 							com_err("rcp", status,
625 								"determining "
626 								"subkey for "
627 								"session");
628 							exit(1);
629 						}
630 						if (!session_key) {
631 							com_err("rcp", 0,
632 								"no subkey "
633 								"negotiated for"
634 								" connection");
635 							exit(1);
636 						}
637 					}
638 					eblock.crypto_entry =
639 						session_key->enctype;
640 					eblock.key =
641 						(krb5_keyblock *)session_key;
642 
643 					init_encrypt(encrypt_flag,
644 						bsd_context, kcmd_proto,
645 						&desinbuf, &desoutbuf, CLIENT,
646 						&eblock);
647 					if (encrypt_flag > 0) {
648 						char *s = gettext("This rcp "
649 							"session is using "
650 							"encryption for all "
651 							"data transmissions."
652 							"\r\n");
653 
654 						(void) write(2, s, strlen(s));
655 					}
656 				}
657 				if (response() < 0)
658 					exit(1);
659 
660 				} else {
661 
662 				/*
663 				 * ACL support: try to find out if the remote
664 				 * site is running acl cognizant version of
665 				 * rcp. A special binary name is used for this
666 				 * purpose.
667 				 */
668 				aclflag = 1;
669 				acl_aclflag = 1;
670 
671 				/*
672 				 * First see if the remote side will support
673 				 * both aclent_t and ace_t acl's?
674 				 */
675 				(void) snprintf(bp, buffersize, "%s -tZ %s",
676 							cmd_sunw, targ);
677 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
678 					    tuser ? tuser : pwd->pw_name,
679 					    bp, 0, AF_INET6);
680 				if (rem < 0)
681 					exit(1);
682 
683 				/*
684 				 * This is similar to routine response().
685 				 * If response is not ok, treat the other
686 				 * side as non-acl rcp.
687 				 */
688 				if (read(rem, &resp, sizeof (resp))
689 				    != sizeof (resp))
690 					lostconn();
691 				if (resp != 0) {
692 					acl_aclflag = 0;
693 					(void) snprintf(bp, buffersize,
694 					    "%s -t %s", cmd_sunw, targ);
695 
696 					(void) close(rem);
697 					host = thost;
698 					rem = rcmd_af(&host, portnumber,
699 					    pwd->pw_name,
700 					    tuser ? tuser : pwd->pw_name,
701 					    bp, 0, AF_INET6);
702 					if (rem < 0)
703 						exit(1);
704 
705 					if (read(rem, &resp, sizeof (resp))
706 					    != sizeof (resp))
707 						lostconn();
708 					if (resp != 0) {
709 						/*
710 						 * Not OK:
711 						 * The other side is running
712 						 * non-acl rcp. Try again with
713 						 * normal stuff
714 						 */
715 						aclflag = 0;
716 						(void) snprintf(bp, buffersize,
717 						    "%s -t %s", cmd, targ);
718 						(void) close(rem);
719 						host = thost;
720 						rem = rcmd_af(&host, portnumber,
721 						    pwd->pw_name,
722 						    tuser ? tuser :
723 						    pwd->pw_name, bp, 0,
724 						    AF_INET6);
725 						if (rem < 0)
726 							exit(1);
727 						if (response() < 0)
728 						    exit(1);
729 					}
730 				}
731 				/* everything should be fine now */
732 				(void) setuid(userid);
733 
734 				}
735 			}
736 			source(1, argv + i);
737 		}
738 	}
739 }
740 
741 static void
742 tolocal(int argc, char *argv[])
743 {
744 	int i;
745 	char *host, *src, *suser, *lhost;
746 	char resp;
747 	size_t buffersize;
748 	char bp[RCP_BUFSIZE];
749 	krb5_creds *cred;
750 	buffersize = RCP_BUFSIZE;
751 
752 	for (i = 0; i < argc - 1; i++) {
753 		if (!(src = colon(argv[i]))) {	/* local to local */
754 			(void) snprintf(bp, buffersize, "%s%s%s%s %s %s",
755 			    _PATH_CP, iamrecursive ? " -r" : "",
756 			    pflag ? " -p" : "",
757 			    zflag ? " -z" : "",
758 			    argv[i], argv[argc - 1]);
759 			if (susystem(bp) == -1)
760 				errs++;
761 			continue;
762 		}
763 		*src++ = 0;
764 		if (*src == 0)
765 			src = ".";
766 		host = search_char((unsigned char *)argv[i], '@');
767 		if (host) {
768 			*host++ = 0;
769 			suser = argv[i];
770 			if (*suser == '\0') {
771 				suser = pwd->pw_name;
772 			} else if (!okname(suser)) {
773 				errs++;
774 				continue;
775 			}
776 		} else {
777 			host = argv[i];
778 			suser = pwd->pw_name;
779 		}
780 		host = removebrackets(host);
781 		lhost = host;
782 		if (krb5auth_flag > 0) {
783 
784 		(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
785 		authopts = AP_OPTS_MUTUAL_REQUIRED;
786 		status = kcmd(&sock, &host,
787 				portnumber,
788 				pwd->pw_name, suser,
789 				bp,
790 				0,	/* &rfd2 */
791 				"host",
792 				krb_realm,
793 				bsd_context,
794 				&auth_context,
795 				&cred,
796 				0,	/* No seq # */
797 				0,	/* No server seq # */
798 				authopts,
799 				1,	/* Not any port # */
800 				&kcmd_proto);
801 		if (status) {
802 			/*
803 			 * If new protocol requested, we dont
804 			 * fallback to less secure ones.
805 			 */
806 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
807 				(void) fprintf(stderr, gettext("rcp: kcmdv2 "
808 					"to host %s failed - %s\n"
809 					"Fallback to normal rcp denied."),
810 					host, error_message(status));
811 				exit(1);
812 			}
813 			if (status != -1) {
814 				(void) fprintf(stderr, gettext("rcp: kcmd "
815 						"to host %s failed - %s,\n"
816 						"trying normal rcp...\n\n"),
817 						host, error_message(status));
818 			} else {
819 				(void) fprintf(stderr,
820 					gettext("trying normal rcp...\n"));
821 			}
822 			/*
823 			 * kcmd() failed, so we have to
824 			 * fallback to normal rcp
825 			 */
826 			try_normal_rcp(prev_argc, prev_argv);
827 		} else {
828 			rem = sock;
829 			session_key = &cred->keyblock;
830 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
831 				status = krb5_auth_con_getlocalsubkey(
832 						bsd_context, auth_context,
833 						&session_key);
834 				if (status) {
835 					com_err("rcp", status, "determining "
836 						"subkey for session");
837 					exit(1);
838 				}
839 				if (!session_key) {
840 					com_err("rcp", 0, "no subkey negotiated"
841 						" for connection");
842 					exit(1);
843 				}
844 			}
845 			eblock.crypto_entry = session_key->enctype;
846 			eblock.key = (krb5_keyblock *)session_key;
847 
848 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
849 					&desinbuf, &desoutbuf, CLIENT,
850 					&eblock);
851 			if (encrypt_flag > 0) {
852 				char *s = gettext("This rcp "
853 					"session is using DES "
854 					"encryption for all "
855 					"data transmissions."
856 					"\r\n");
857 
858 				(void) write(2, s, strlen(s));
859 			}
860 		}
861 
862 		}
863 		else
864 		{
865 
866 		/*
867 		 * ACL support: try to find out if the remote site is
868 		 * running acl cognizant version of rcp.
869 		 */
870 		aclflag = 1;
871 		acl_aclflag = 1;
872 
873 		(void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, src);
874 		rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
875 			    bp, 0, AF_INET6);
876 
877 		if (rem < 0) {
878 			++errs;
879 			continue;
880 		}
881 
882 		/*
883 		 * The remote system is supposed to send an ok response.
884 		 * If there are any data other than "ok", it must be error
885 		 * messages from the remote system. We can assume the
886 		 * remote system is running non-acl version rcp.
887 		 */
888 		if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
889 			lostconn();
890 		if (resp != 0) {
891 
892 			/*
893 			 * Try again without ace_acl support
894 			 */
895 			acl_aclflag = 0;
896 			(void) snprintf(bp, buffersize, "%s -f %s",
897 			    cmd_sunw, src);
898 			rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
899 			    bp, 0, AF_INET6);
900 
901 			if (rem < 0) {
902 				++errs;
903 				continue;
904 			}
905 
906 			if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
907 				lostconn();
908 
909 			/*
910 			 * NOT ok:
911 			 * The other side is running non-acl rcp.
912 			 * Try again with normal stuff
913 			 */
914 			aclflag = 0;
915 			(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
916 				(void) close(rem);
917 				host = lhost;
918 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
919 						suser, bp, 0, AF_INET6);
920 			if (rem < 0) {
921 				++errs;
922 				continue;
923 			}
924 		}
925 		}
926 
927 		sink(1, argv + argc - 1);
928 
929 		(void) close(rem);
930 		rem = -1;
931 	}
932 }
933 
934 
935 static void
936 verifydir(char *cp)
937 {
938 	struct stat stb;
939 
940 	if (stat(cp, &stb) >= 0) {
941 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
942 			return;
943 		errno = ENOTDIR;
944 	}
945 	error("rcp: %s: %s.\n", cp, strerror(errno));
946 	exit(1);
947 }
948 
949 static char *
950 colon(char *cp)
951 {
952 	boolean_t is_bracket_open = B_FALSE;
953 
954 	for (; *cp; ++cp) {
955 		if (*cp == '[')
956 			is_bracket_open = B_TRUE;
957 		else if (*cp == ']')
958 			is_bracket_open = B_FALSE;
959 		else if (*cp == ':' && !is_bracket_open)
960 			return (cp);
961 		else if (*cp == '/')
962 			return (0);
963 	}
964 	return (0);
965 }
966 
967 static int
968 okname(char *cp0)
969 {
970 	register char *cp = cp0;
971 	register int c;
972 
973 	do {
974 		c = *cp;
975 		if (c & 0200)
976 			goto bad;
977 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
978 			goto bad;
979 	} while (*++cp);
980 	return (1);
981 bad:
982 	(void) fprintf(stderr, "rcp: invalid user name %s\n", cp0);
983 	return (0);
984 }
985 
986 
987 static char *
988 removebrackets(char *str)
989 {
990 	char *newstr = str;
991 
992 	if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
993 		newstr = str + 1;
994 		str[strlen(str) - 1] = '\0';
995 	}
996 	return (newstr);
997 }
998 
999 static int
1000 susystem(char *s)
1001 {
1002 	int status, pid, w;
1003 	register void (*istat)(), (*qstat)();
1004 	int pfds[2];
1005 	char buf[BUFSIZ];
1006 	int cnt;
1007 	boolean_t seen_stderr_traffic;
1008 
1009 	/*
1010 	 * Due to the fact that rcp uses rsh to copy between 2 remote
1011 	 * machines, rsh doesn't return the exit status of the remote
1012 	 * command, and we can't modify the rcmd protocol used by rsh
1013 	 * (for interoperability reasons) we use the hack of using any
1014 	 * output on stderr as indication that an error occurred and
1015 	 * that we should return a non-zero error code.
1016 	 */
1017 
1018 	if (pipe(pfds) == -1) {
1019 		(void) fprintf(stderr, "Couldn't create pipe: %s\n",
1020 		    strerror(errno));
1021 		return (-1);
1022 	}
1023 
1024 	if ((pid = vfork()) < 0) {
1025 		(void) close(pfds[0]);
1026 		(void) close(pfds[1]);
1027 		(void) fprintf(stderr, "Couldn't fork child process: %s\n",
1028 		    strerror(errno));
1029 		return (-1);
1030 	} else if (pid == 0) {
1031 		/*
1032 		 * Child.
1033 		 */
1034 		(void) close(pfds[0]);
1035 		/*
1036 		 * Send stderr messages down the pipe so that we can detect
1037 		 * them in the parent process.
1038 		 */
1039 		if (pfds[1] != STDERR_FILENO) {
1040 			(void) dup2(pfds[1], STDERR_FILENO);
1041 			(void) close(pfds[1]);
1042 		}
1043 		/*
1044 		 * This shell does not inherit the additional privilege
1045 		 * we have in our Permitted set.
1046 		 */
1047 		(void) execl(_PATH_BSHELL, "sh", "-c", s, (char *)0);
1048 		_exit(127);
1049 	}
1050 	/*
1051 	 * Parent.
1052 	 */
1053 	istat = signal(SIGINT, SIG_IGN);
1054 	qstat = signal(SIGQUIT, SIG_IGN);
1055 
1056 	(void) close(pfds[1]);
1057 	seen_stderr_traffic = B_FALSE;
1058 	while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) {
1059 		/*
1060 		 * If any data is read from the pipe the child process
1061 		 * has output something on stderr so we set the boolean
1062 		 * 'seen_stderr_traffic' to true, which will cause the
1063 		 * function to return -1.
1064 		 */
1065 		(void) write(STDERR_FILENO, buf, cnt);
1066 		seen_stderr_traffic = B_TRUE;
1067 	}
1068 	(void) close(pfds[0]);
1069 	while ((w = wait(&status)) != pid && w != -1)
1070 		;
1071 	if (w == -1)
1072 		status = -1;
1073 
1074 	(void) signal(SIGINT, istat);
1075 	(void) signal(SIGQUIT, qstat);
1076 
1077 	return (seen_stderr_traffic ? -1 : status);
1078 }
1079 
1080 static void
1081 source(int argc, char *argv[])
1082 {
1083 	struct stat stb;
1084 	static BUF buffer;
1085 	BUF *bp;
1086 	int x, readerr, f, amt;
1087 	char *last, *name, buf[RCP_BUFSIZE];
1088 	off_t off, size, i;
1089 	ssize_t cnt;
1090 
1091 	for (x = 0; x < argc; x++) {
1092 		name = argv[x];
1093 		if ((f = open(name, O_RDONLY, 0)) < 0) {
1094 			error("rcp: %s: %s\n", name, strerror(errno));
1095 			continue;
1096 		}
1097 		if (fstat(f, &stb) < 0)
1098 			goto notreg;
1099 		switch (stb.st_mode&S_IFMT) {
1100 
1101 		case S_IFREG:
1102 			break;
1103 
1104 		case S_IFDIR:
1105 			if (iamrecursive) {
1106 				(void) close(f);
1107 				rsource(name, &stb);
1108 				continue;
1109 			}
1110 			/* FALLTHROUGH */
1111 		default:
1112 notreg:
1113 			(void) close(f);
1114 			error("rcp: %s: not a plain file\n", name);
1115 			continue;
1116 		}
1117 		last = rindex(name, '/');
1118 		if (last == 0)
1119 			last = name;
1120 		else
1121 			last++;
1122 		if (pflag) {
1123 			time_t mtime, atime;
1124 			time_t now;
1125 
1126 			/*
1127 			 * Make it compatible with possible future
1128 			 * versions expecting microseconds.
1129 			 */
1130 			mtime = stb.st_mtime;
1131 			atime = stb.st_atime;
1132 
1133 			if ((mtime < 0) || (atime < 0)) {
1134 				now = time(NULL);
1135 
1136 				if (mtime < 0) {
1137 					mtime = now;
1138 					error("negative modification time on "
1139 					    "%s; not preserving\n", name);
1140 				}
1141 				if (atime < 0) {
1142 					atime = now;
1143 					error("negative access time on "
1144 					    "%s; not preserving\n", name);
1145 				}
1146 			}
1147 			(void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n",
1148 							mtime, atime);
1149 			(void) desrcpwrite(rem, buf, strlen(buf));
1150 			if (response() < 0) {
1151 				(void) close(f);
1152 				continue;
1153 			}
1154 		}
1155 		(void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
1156 			(uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size,
1157 			last);
1158 		(void) desrcpwrite(rem, buf, strlen(buf));
1159 		if (response() < 0) {
1160 			(void) close(f);
1161 			continue;
1162 		}
1163 
1164 		/* ACL support: send */
1165 		if (aclflag | acl_aclflag) {
1166 			/* get acl from f and send it over */
1167 			if (sendacl(f) == ACL_FAIL) {
1168 				(void) close(f);
1169 				continue;
1170 			}
1171 		}
1172 		if ((krb5auth_flag > 0) || (iamremote == 1)) {
1173 			bp = allocbuf(&buffer, f, RCP_BUFSIZE);
1174 			if (bp == NULLBUF) {
1175 				(void) close(f);
1176 				continue;
1177 			}
1178 			readerr = 0;
1179 			for (i = 0; i < stb.st_size; i += bp->cnt) {
1180 				amt = bp->cnt;
1181 				if (i + amt > stb.st_size)
1182 					amt = stb.st_size - i;
1183 				if (readerr == 0 &&
1184 				    read(f, bp->buf, amt) != amt)
1185 					readerr = errno;
1186 				(void) desrcpwrite(rem, bp->buf, amt);
1187 			}
1188 			(void) close(f);
1189 			if (readerr == 0)
1190 				(void) desrcpwrite(rem, "", 1);
1191 			else
1192 				error("rcp: %s: %s\n", name,
1193 				    error_message(readerr));
1194 		} else {
1195 			cnt = off = 0;
1196 			size = stb.st_size;
1197 			while (size != 0) {
1198 				amt = MIN(size, SENDFILE_SIZE);
1199 				cnt = sendfile(rem, f, &off, amt);
1200 				if (cnt == -1) {
1201 					if (errno == EINTR) {
1202 						continue;
1203 					} else {
1204 						break;
1205 					}
1206 				}
1207 				size -= cnt;
1208 			}
1209 			if (cnt == -1) {
1210 				error("rcp: %s: %s\n", name, strerror(errno));
1211 			} else {
1212 				(void) write(rem, "", 1);
1213 			}
1214 			(void) close(f);
1215 		}
1216 		(void) response();
1217 	}
1218 }
1219 
1220 
1221 static void
1222 rsource(char *name, struct stat *statp)
1223 {
1224 	DIR *d;
1225 	struct dirent *dp;
1226 	char *last, *vect[1];
1227 	char path[MAXPATHLEN];
1228 
1229 	if (!(d = opendir(name))) {
1230 		error("rcp: %s: %s\n", name, strerror(errno));
1231 		return;
1232 	}
1233 	last = rindex(name, '/');
1234 	if (last == 0)
1235 		last = name;
1236 	else
1237 		last++;
1238 	if (pflag) {
1239 		(void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n",
1240 				statp->st_mtime, statp->st_atime);
1241 		(void) desrcpwrite(rem, path, strlen(path));
1242 		if (response() < 0) {
1243 			(void) closedir(d);
1244 			return;
1245 		}
1246 	}
1247 	(void) snprintf(path, sizeof (path), "D%04o %d %s\n",
1248 	    (uint_t)(statp->st_mode & 07777), 0, last);
1249 	(void) desrcpwrite(rem, path, strlen(path));
1250 
1251 	/* acl support for directory */
1252 	if (aclflag) {
1253 		/* get acl from f and send it over */
1254 		if (sendacl(d->dd_fd) == ACL_FAIL) {
1255 			(void) closedir(d);
1256 			return;
1257 		}
1258 	}
1259 
1260 	if (response() < 0) {
1261 		(void) closedir(d);
1262 		return;
1263 	}
1264 
1265 	while (dp = readdir(d)) {
1266 		if (dp->d_ino == 0)
1267 			continue;
1268 		if ((strcmp(dp->d_name, ".") == 0) ||
1269 		    (strcmp(dp->d_name, "..") == 0))
1270 			continue;
1271 		if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >=
1272 			MAXPATHLEN - 1) {
1273 			error("%s/%s: name too long.\n", name, dp->d_name);
1274 			continue;
1275 		}
1276 		(void) snprintf(path, sizeof (path), "%s/%s",
1277 					name, dp->d_name);
1278 		vect[0] = path;
1279 		source(1, vect);
1280 	}
1281 	(void) closedir(d);
1282 	(void) desrcpwrite(rem, "E\n", 2);
1283 	(void) response();
1284 }
1285 
1286 static int
1287 response(void)
1288 {
1289 	register char *cp;
1290 	char ch, resp, rbuf[RCP_BUFSIZE];
1291 
1292 	if (desrcpread(rem, &resp, 1) != 1)
1293 		lostconn();
1294 	cp = rbuf;
1295 	switch (resp) {
1296 	case 0:				/* ok */
1297 		return (0);
1298 	default:
1299 		*cp++ = resp;
1300 		/* FALLTHROUGH */
1301 	case 1:				/* error, followed by err msg */
1302 	case 2:				/* fatal error, "" */
1303 		do {
1304 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1305 				lostconn();
1306 			*cp++ = ch;
1307 		} while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n');
1308 
1309 		if (!iamremote)
1310 			(void) write(STDERR_FILENO, rbuf, cp - rbuf);
1311 		++errs;
1312 		if (resp == 1)
1313 			return (-1);
1314 		exit(1);
1315 	}
1316 	/*NOTREACHED*/
1317 }
1318 
1319 static void
1320 lostconn(void)
1321 {
1322 	if (!iamremote)
1323 		(void) fprintf(stderr, "rcp: lost connection\n");
1324 	exit(1);
1325 }
1326 
1327 
1328 static void
1329 sink(int argc, char *argv[])
1330 {
1331 	char *cp;
1332 	static BUF buffer;
1333 	struct stat stb;
1334 	struct timeval tv[2];
1335 	BUF *bp;
1336 	off_t i, j;
1337 	char ch, *targ, *why;
1338 	int amt, count, exists, first, mask, mode;
1339 	off_t size;
1340 	int ofd, setimes, targisdir, wrerr;
1341 	char *np, *vect[1], buf[RCP_BUFSIZE];
1342 	char *namebuf = NULL;
1343 	size_t namebuf_sz = 0;
1344 	size_t need;
1345 
1346 #define	atime	tv[0]
1347 #define	mtime	tv[1]
1348 #define	SCREWUP(str)	{ why = str; goto screwup; }
1349 
1350 	setimes = targisdir = 0;
1351 	mask = umask(0);
1352 	if (!pflag)
1353 		(void) umask(mask);
1354 	if (argc != 1) {
1355 		error("rcp: ambiguous target\n");
1356 		exit(1);
1357 	}
1358 	targ = *argv;
1359 	if (targetshouldbedirectory)
1360 		verifydir(targ);
1361 	(void) desrcpwrite(rem, "", 1);
1362 
1363 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
1364 		targisdir = 1;
1365 	for (first = 1; ; first = 0) {
1366 		cp = buf;
1367 		if (desrcpread(rem, cp, 1) <= 0) {
1368 			if (namebuf != NULL)
1369 				free(namebuf);
1370 			return;
1371 		}
1372 
1373 		if (*cp++ == '\n')
1374 			SCREWUP("unexpected <newline>");
1375 		do {
1376 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1377 				SCREWUP("lost connection");
1378 			*cp++ = ch;
1379 		} while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n');
1380 		*cp = 0;
1381 
1382 		if (buf[0] == '\01' || buf[0] == '\02') {
1383 			if (iamremote == 0)
1384 				(void) write(STDERR_FILENO, buf + 1,
1385 				    strlen(buf + 1));
1386 			if (buf[0] == '\02')
1387 				exit(1);
1388 			errs++;
1389 			continue;
1390 		}
1391 		if (buf[0] == 'E') {
1392 			(void) desrcpwrite(rem, "", 1);
1393 			if (namebuf != NULL)
1394 				free(namebuf);
1395 			return;
1396 		}
1397 
1398 		if (ch == '\n')
1399 			*--cp = 0;
1400 		cp = buf;
1401 		if (*cp == 'T') {
1402 			setimes++;
1403 			cp++;
1404 			mtime.tv_sec = strtol(cp, &cp, 0);
1405 			if (*cp++ != ' ')
1406 				SCREWUP("mtime.sec not delimited");
1407 			mtime.tv_usec = strtol(cp, &cp, 0);
1408 			if (*cp++ != ' ')
1409 				SCREWUP("mtime.usec not delimited");
1410 			atime.tv_sec = strtol(cp, &cp, 0);
1411 			if (*cp++ != ' ')
1412 				SCREWUP("atime.sec not delimited");
1413 			atime.tv_usec = strtol(cp, &cp, 0);
1414 			if (*cp++ != '\0')
1415 				SCREWUP("atime.usec not delimited");
1416 			(void) desrcpwrite(rem, "", 1);
1417 			continue;
1418 		}
1419 		if (*cp != 'C' && *cp != 'D') {
1420 			/*
1421 			 * Check for the case "rcp remote:foo\* local:bar".
1422 			 * In this case, the line "No match." can be returned
1423 			 * by the shell before the rcp command on the remote is
1424 			 * executed so the ^Aerror_message convention isn't
1425 			 * followed.
1426 			 */
1427 			if (first) {
1428 				error("%s\n", cp);
1429 				exit(1);
1430 			}
1431 			SCREWUP("expected control record");
1432 		}
1433 		mode = 0;
1434 		for (++cp; cp < buf + 5; cp++) {
1435 			if (*cp < '0' || *cp > '7')
1436 				SCREWUP("bad mode");
1437 			mode = (mode << 3) | (*cp - '0');
1438 		}
1439 		if (*cp++ != ' ')
1440 			SCREWUP("mode not delimited");
1441 		size = 0;
1442 		while (isdigit(*cp))
1443 			size = size * 10 + (*cp++ - '0');
1444 		if (*cp++ != ' ')
1445 			SCREWUP("size not delimited");
1446 		if (targisdir) {
1447 			need = strlen(targ) + sizeof ("/") + strlen(cp);
1448 			if (need > namebuf_sz) {
1449 			    if ((namebuf = realloc(namebuf, need)) == NULL) {
1450 					error("rcp: out of memory\n");
1451 					exit(1);
1452 			    }
1453 			    namebuf_sz = need;
1454 			}
1455 			(void) snprintf(namebuf, need, "%s%s%s", targ,
1456 			    *targ ? "/" : "", cp);
1457 			np = namebuf;
1458 		} else {
1459 			np = targ;
1460 		}
1461 
1462 		exists = stat(np, &stb) == 0;
1463 		if (buf[0] == 'D') {
1464 			if (exists) {
1465 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
1466 					if (aclflag | acl_aclflag) {
1467 						/*
1468 						 * consume acl in the pipe
1469 						 * fd = -1 to indicate the
1470 						 * special case
1471 						 */
1472 						if (recvacl(-1, exists, pflag)
1473 						    == ACL_FAIL) {
1474 							goto bad;
1475 						}
1476 					}
1477 					errno = ENOTDIR;
1478 					goto bad;
1479 				}
1480 				if (pflag)
1481 					(void) chmod(np, mode);
1482 			} else if (mkdir(np, mode) < 0) {
1483 				if (aclflag) {
1484 					/* consume acl in the pipe */
1485 					(void) recvacl(-1, exists, pflag);
1486 				}
1487 				goto bad;
1488 			}
1489 
1490 			/* acl support for directories */
1491 			if (aclflag | acl_aclflag) {
1492 				int dfd;
1493 
1494 				if ((dfd = open(np, O_RDONLY)) == -1)
1495 					goto bad;
1496 
1497 				/* get acl and set it to ofd */
1498 				if (recvacl(dfd, exists, pflag) == ACL_FAIL) {
1499 					(void) close(dfd);
1500 					if (!exists)
1501 						(void) rmdir(np);
1502 					goto bad;
1503 				}
1504 				(void) close(dfd);
1505 			}
1506 
1507 			vect[0] = np;
1508 			sink(1, vect);
1509 			if (setimes) {
1510 				setimes = 0;
1511 				if (utimes(np, tv) < 0)
1512 				    error("rcp: can't set times on %s: %s\n",
1513 					np, strerror(errno));
1514 			}
1515 			continue;
1516 		}
1517 
1518 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
1519 bad:
1520 			error("rcp: %s: %s\n", np, strerror(errno));
1521 			continue;
1522 		}
1523 
1524 		/*
1525 		 * If the output file exists we have to force zflag off
1526 		 * to avoid erroneously seeking past old data.
1527 		 */
1528 		zopen(ofd, zflag && !exists);
1529 
1530 		if (exists && pflag)
1531 			(void) fchmod(ofd, mode);
1532 
1533 		(void) desrcpwrite(rem, "", 1);
1534 
1535 		/*
1536 		 * ACL support: receiving
1537 		 */
1538 		if (aclflag | acl_aclflag) {
1539 			/* get acl and set it to ofd */
1540 			if (recvacl(ofd, exists, pflag) == ACL_FAIL) {
1541 				(void) close(ofd);
1542 				if (!exists)
1543 					(void) unlink(np);
1544 				continue;
1545 			}
1546 		}
1547 
1548 		if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) {
1549 			(void) close(ofd);
1550 			continue;
1551 		}
1552 		cp = bp->buf;
1553 		count = 0;
1554 		wrerr = 0;
1555 		for (i = 0; i < size; i += RCP_BUFSIZE) {
1556 			amt = RCP_BUFSIZE;
1557 			if (i + amt > size)
1558 				amt = size - i;
1559 			count += amt;
1560 			do {
1561 				j = desrcpread(rem, cp, amt);
1562 				if (j <= 0) {
1563 					int sverrno = errno;
1564 
1565 					/*
1566 					 * Connection to supplier lost.
1567 					 * Truncate file to correspond
1568 					 * to amount already transferred.
1569 					 *
1570 					 * Note that we must call ftruncate()
1571 					 * before any call to error() (which
1572 					 * might result in a SIGPIPE and
1573 					 * sudden death before we have a chance
1574 					 * to correct the file's size).
1575 					 */
1576 					size = lseek(ofd, 0, SEEK_CUR);
1577 					if ((ftruncate(ofd, size)  == -1) &&
1578 					    (errno != EINVAL) &&
1579 					    (errno != EACCES))
1580 #define		TRUNCERR	"rcp: can't truncate %s: %s\n"
1581 						error(TRUNCERR, np,
1582 						    strerror(errno));
1583 					error("rcp: %s\n",
1584 					    j ? strerror(sverrno) :
1585 					    "dropped connection");
1586 					(void) close(ofd);
1587 					exit(1);
1588 				}
1589 				amt -= j;
1590 				cp += j;
1591 			} while (amt > 0);
1592 			if (count == bp->cnt) {
1593 				cp = bp->buf;
1594 				if (wrerr == 0 &&
1595 				    zwrite(ofd, cp, count) < 0)
1596 					wrerr++;
1597 				count = 0;
1598 			}
1599 		}
1600 		if (count != 0 && wrerr == 0 &&
1601 		    zwrite(ofd, bp->buf, count) < 0)
1602 			wrerr++;
1603 		if (zclose(ofd) < 0)
1604 			wrerr++;
1605 
1606 
1607 		if ((ftruncate(ofd, size)  == -1) && (errno != EINVAL) &&
1608 		    (errno != EACCES)) {
1609 			error(TRUNCERR, np, strerror(errno));
1610 		}
1611 		(void) close(ofd);
1612 		(void) response();
1613 		if (setimes) {
1614 			setimes = 0;
1615 			if (utimes(np, tv) < 0)
1616 				error("rcp: can't set times on %s: %s\n",
1617 				    np, strerror(errno));
1618 		}
1619 		if (wrerr)
1620 			error("rcp: %s: %s\n", np, strerror(errno));
1621 		else
1622 			(void) desrcpwrite(rem, "", 1);
1623 	}
1624 screwup:
1625 	error("rcp: protocol screwup: %s\n", why);
1626 	exit(1);
1627 }
1628 
1629 #ifndef roundup
1630 #define	roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
1631 #endif /* !roundup */
1632 
1633 static BUF *
1634 allocbuf(BUF *bp, int fd, int blksize)
1635 {
1636 	struct stat stb;
1637 	int size;
1638 
1639 	if (fstat(fd, &stb) < 0) {
1640 		error("rcp: fstat: %s\n", strerror(errno));
1641 		return (0);
1642 	}
1643 	size = roundup(stb.st_blksize, blksize);
1644 	if (size == 0)
1645 		size = blksize;
1646 	if (bp->cnt < size) {
1647 		if (bp->buf != 0)
1648 			free(bp->buf);
1649 		bp->buf = (char *)malloc((uint_t)size);
1650 		if (!bp->buf) {
1651 			error("rcp: malloc: out of memory\n");
1652 			return (0);
1653 		}
1654 	}
1655 	bp->cnt = size;
1656 	return (bp);
1657 }
1658 
1659 static void
1660 usage(void)
1661 {
1662 	(void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"),
1663 		gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
1664 #ifdef DEBUG
1665 			"[-D port] "
1666 #endif /* DEBUG */
1667 			"f1 f2; or:\n"),
1668 		gettext("\trcp [-r] [-p] [-a] [-x] "
1669 #ifdef DEBUG
1670 			"[-D port] "
1671 #endif /* DEBUG */
1672 			"[-k realm] [-PN / -PO] f1...fn d2\n"));
1673 	exit(1);
1674 }
1675 
1676 
1677 /*
1678  * sparse file support
1679  */
1680 
1681 static off_t zbsize;
1682 static off_t zlastseek;
1683 
1684 /* is it ok to try to create holes? */
1685 static void
1686 zopen(int fd, int flag)
1687 {
1688 	struct stat st;
1689 
1690 	zbsize = 0;
1691 	zlastseek = 0;
1692 
1693 	if (flag &&
1694 		fstat(fd, &st) == 0 &&
1695 		(st.st_mode & S_IFMT) == S_IFREG)
1696 		zbsize = st.st_blksize;
1697 }
1698 
1699 /* write and/or seek */
1700 static int
1701 zwrite(int fd, char *buf, int nbytes)
1702 {
1703 	off_t block = zbsize ? zbsize : nbytes;
1704 
1705 	do {
1706 		if (block > nbytes)
1707 			block = nbytes;
1708 		nbytes -= block;
1709 
1710 		if (!zbsize || notzero(buf, block)) {
1711 			register int n, count = block;
1712 
1713 			do {
1714 				if ((n = write(fd, buf, count)) < 0)
1715 					return (-1);
1716 				buf += n;
1717 			} while ((count -= n) > 0);
1718 			zlastseek = 0;
1719 		} else {
1720 			if (lseek(fd, (off_t)block, SEEK_CUR) < 0)
1721 				return (-1);
1722 			buf += block;
1723 			zlastseek = 1;
1724 		}
1725 	} while (nbytes > 0);
1726 
1727 	return (0);
1728 }
1729 
1730 /* write last byte of file if necessary */
1731 static int
1732 zclose(int fd)
1733 {
1734 	zbsize = 0;
1735 
1736 	if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 ||
1737 		zwrite(fd, "", 1) < 0))
1738 		return (-1);
1739 	else
1740 		return (0);
1741 }
1742 
1743 /* return true if buffer is not all zeros */
1744 static int
1745 notzero(char *p, int n)
1746 {
1747 	register int result = 0;
1748 
1749 	while ((int)p & 3 && --n >= 0)
1750 		result |= *p++;
1751 
1752 	while ((n -= 4 * sizeof (int)) >= 0) {
1753 		/* LINTED */
1754 		result |= ((int *)p)[0];
1755 		/* LINTED */
1756 		result |= ((int *)p)[1];
1757 		/* LINTED */
1758 		result |= ((int *)p)[2];
1759 		/* LINTED */
1760 		result |= ((int *)p)[3];
1761 		if (result)
1762 			return (result);
1763 		p += 4 * sizeof (int);
1764 	}
1765 	n += 4 * sizeof (int);
1766 
1767 	while (--n >= 0)
1768 		result |= *p++;
1769 
1770 	return (result);
1771 }
1772 
1773 /*
1774  * New functions to support ACLs
1775  */
1776 
1777 /*
1778  * Get acl from f and send it over.
1779  * ACL record includes acl entry count, acl text length, and acl text.
1780  */
1781 static int
1782 sendacl(int f)
1783 {
1784 	int		aclcnt;
1785 	char		*acltext;
1786 	char		buf[BUFSIZ];
1787 	acl_t		*aclp;
1788 	char		acltype;
1789 	int		aclerror;
1790 	int		trivial;
1791 
1792 
1793 	aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp);
1794 	if (aclerror != 0) {
1795 		error("can't retrieve ACL: %s \n", acl_strerror(aclerror));
1796 		return (ACL_FAIL);
1797 	}
1798 
1799 	/*
1800 	 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0
1801 	 * then don't do the malloc and facl(fd, getcntcmd,...);
1802 	 * since the remote side doesn't support alternate style ACL's.
1803 	 */
1804 
1805 	if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) {
1806 		aclcnt = MIN_ACL_ENTRIES;
1807 		acltype = 'A';
1808 		trivial = ACL_IS_TRIVIAL;
1809 	} else {
1810 
1811 		aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0;
1812 
1813 		if (aclp) {
1814 			acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A';
1815 			aclcnt = acl_cnt(aclp);
1816 			trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL);
1817 		} else {
1818 			acltype = 'A';
1819 			aclcnt = MIN_ACL_ENTRIES;
1820 			trivial = ACL_IS_TRIVIAL;
1821 		}
1822 
1823 	}
1824 
1825 	/* send the acl count over */
1826 	(void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt);
1827 	(void) desrcpwrite(rem, buf, strlen(buf));
1828 
1829 	/*
1830 	 * only send acl when we have an aclp, which would
1831 	 * imply its not trivial.
1832 	 */
1833 	if (aclp && (trivial != ACL_IS_TRIVIAL)) {
1834 		acltext = acl_totext(aclp, 0);
1835 		if (acltext == NULL) {
1836 			error("rcp: failed to convert to text\n");
1837 			acl_free(aclp);
1838 			return (ACL_FAIL);
1839 		}
1840 
1841 		/* send ACLs over: send the length first */
1842 		(void) snprintf(buf, sizeof (buf), "%c%d\n",
1843 		    acltype, strlen(acltext));
1844 
1845 		(void) desrcpwrite(rem, buf, strlen(buf));
1846 		(void) desrcpwrite(rem, acltext, strlen(acltext));
1847 		free(acltext);
1848 		if (response() < 0) {
1849 			acl_free(aclp);
1850 			return (ACL_FAIL);
1851 		}
1852 
1853 	}
1854 
1855 	if (aclp)
1856 		acl_free(aclp);
1857 	return (ACL_OK);
1858 }
1859 
1860 /*
1861  * Use this routine to get acl entry count and acl text size (in bytes)
1862  */
1863 static int
1864 getaclinfo(int *cnt, int *acltype)
1865 {
1866 	char		buf[BUFSIZ];
1867 	char		*cp;
1868 	char		ch;
1869 
1870 	/* get acl count */
1871 	cp = buf;
1872 	if (desrcpread(rem, cp, 1) <= 0)
1873 		return (ACL_FAIL);
1874 
1875 	switch (*cp++) {
1876 	case 'A':
1877 		*acltype = 0;
1878 		break;
1879 	case 'Z':
1880 		*acltype = 1;
1881 		break;
1882 	default:
1883 		error("rcp: expect an ACL record, but got %c\n", *cp);
1884 		return (ACL_FAIL);
1885 	}
1886 	do {
1887 		if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) {
1888 			error("rcp: lost connection ..\n");
1889 			return (ACL_FAIL);
1890 		}
1891 		*cp++ = ch;
1892 	} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
1893 	if (ch != '\n') {
1894 		error("rcp: ACL record corrupted \n");
1895 		return (ACL_FAIL);
1896 	}
1897 	cp = &buf[1];
1898 	*cnt = strtol(cp, &cp, 0);
1899 	if (*cp != '\n') {
1900 		error("rcp: ACL record corrupted \n");
1901 		return (ACL_FAIL);
1902 	}
1903 	return (ACL_OK);
1904 }
1905 
1906 
1907 /*
1908  * Receive acl from the pipe and set it to f
1909  */
1910 static int
1911 recvacl(int f, int exists, int preserve)
1912 {
1913 	int		aclcnt;		/* acl entry count */
1914 	int		aclsize;	/* acl text length */
1915 	int		j;
1916 	char		*tp;
1917 	char		*acltext;	/* external format */
1918 	acl_t		*aclp;
1919 	int		acltype;
1920 	int		min_entries;
1921 	int		aclerror;
1922 
1923 	/* get acl count */
1924 	if (getaclinfo(&aclcnt, &acltype) != ACL_OK)
1925 		return (ACL_FAIL);
1926 
1927 	if (acltype == 0) {
1928 		min_entries = MIN_ACL_ENTRIES;
1929 	} else {
1930 		min_entries = 1;
1931 	}
1932 
1933 	if (aclcnt > min_entries) {
1934 		/* get acl text size */
1935 		if (getaclinfo(&aclsize, &acltype) != ACL_OK)
1936 			return (ACL_FAIL);
1937 		if ((acltext = malloc(aclsize + 1)) == NULL) {
1938 			error("rcp: cant allocate memory: %d\n", aclsize);
1939 			return (ACL_FAIL);
1940 		}
1941 
1942 		tp = acltext;
1943 		do {
1944 			j = desrcpread(rem, tp, aclsize);
1945 			if (j <= 0) {
1946 				error("rcp: %s\n", j ? strerror(errno) :
1947 				    "dropped connection");
1948 				exit(1);
1949 			}
1950 			aclsize -= j;
1951 			tp += j;
1952 		} while (aclsize > 0);
1953 		*tp = '\0';
1954 
1955 		if (preserve || !exists) {
1956 			aclerror = acl_fromtext(acltext, &aclp);
1957 			if (aclerror != 0) {
1958 				error("rcp: failed to parse acl : %s\n",
1959 				    acl_strerror(aclerror));
1960 				return (ACL_FAIL);
1961 			}
1962 
1963 			if (f != -1) {
1964 				if (facl_set(f, aclp) < 0) {
1965 					error("rcp: failed to set acl\n");
1966 					return (ACL_FAIL);
1967 				}
1968 			}
1969 			/* -1 means that just consume the data in the pipe */
1970 			acl_free(aclp);
1971 		}
1972 		free(acltext);
1973 		(void) desrcpwrite(rem, "", 1);
1974 	}
1975 	return (ACL_OK);
1976 }
1977 
1978 
1979 static char *
1980 search_char(unsigned char *cp, unsigned char chr)
1981 {
1982 	int	len;
1983 
1984 	while (*cp) {
1985 		if (*cp == chr)
1986 			return ((char *)cp);
1987 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
1988 			len = 1;
1989 		cp += len;
1990 	}
1991 	return (0);
1992 }
1993 
1994 
1995 static int
1996 desrcpread(int fd, char *buf, int len)
1997 {
1998 	return ((int)desread(fd, buf, len, 0));
1999 }
2000 
2001 static int
2002 desrcpwrite(int fd, char *buf, int len)
2003 {
2004 	/*
2005 	 * Note that rcp depends on the same file descriptor being both
2006 	 * input and output to the remote side.  This is bogus, especially
2007 	 * when rcp is being run by a rsh that pipes. Fix it here because
2008 	 * it would require significantly more work in other places.
2009 	 * --hartmans 1/96
2010 	 */
2011 
2012 	if (fd == 0)
2013 		fd = 1;
2014 	return ((int)deswrite(fd, buf, len, 0));
2015 }
2016 
2017 static char **
2018 save_argv(int argc, char **argv)
2019 {
2020 	int i;
2021 
2022 	char **local_argv = (char **)calloc((unsigned)argc + 1,
2023 	    (unsigned)sizeof (char *));
2024 
2025 	/*
2026 	 * allocate an extra pointer, so that it is initialized to NULL and
2027 	 * execv() will work
2028 	 */
2029 	for (i = 0; i < argc; i++) {
2030 		local_argv[i] = strsave(argv[i]);
2031 	}
2032 
2033 	return (local_argv);
2034 }
2035 
2036 #define	SIZEOF_INADDR sizeof (struct in_addr)
2037 
2038 static void
2039 answer_auth(char *config_file, char *ccache_file)
2040 {
2041 	krb5_data pname_data, msg;
2042 	krb5_creds creds, *new_creds;
2043 	krb5_ccache cc;
2044 	krb5_auth_context auth_context = NULL;
2045 
2046 	if (config_file) {
2047 		const char *filenames[2];
2048 
2049 		filenames[1] = NULL;
2050 		filenames[0] = config_file;
2051 		if (krb5_set_config_files(bsd_context, filenames))
2052 			exit(1);
2053 	}
2054 	(void) memset((char *)&creds, 0, sizeof (creds));
2055 
2056 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data))
2057 		exit(1);
2058 
2059 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem,
2060 	    &creds.second_ticket))
2061 		exit(1);
2062 
2063 	if (ccache_file == NULL) {
2064 		if (krb5_cc_default(bsd_context, &cc))
2065 			exit(1);
2066 	} else {
2067 		if (krb5_cc_resolve(bsd_context, ccache_file, &cc))
2068 			exit(1);
2069 	}
2070 
2071 	if (krb5_cc_get_principal(bsd_context, cc, &creds.client))
2072 		exit(1);
2073 
2074 	if (krb5_parse_name(bsd_context, pname_data.data, &creds.server))
2075 		exit(1);
2076 
2077 	krb5_xfree(pname_data.data);
2078 	if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds,
2079 	    &new_creds))
2080 		exit(1);
2081 
2082 	if (krb5_mk_req_extended(bsd_context, &auth_context,
2083 	    AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))
2084 		exit(1);
2085 
2086 	if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) {
2087 		krb5_xfree(msg.data);
2088 		exit(1);
2089 	}
2090 	/* setup eblock for des_read and write */
2091 	krb5_copy_keyblock(bsd_context, &new_creds->keyblock, &session_key);
2092 
2093 	/* OK process key */
2094 	eblock.crypto_entry = session_key->enctype;
2095 	eblock.key = (krb5_keyblock *)session_key;
2096 
2097 	init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL,
2098 	    &desinbuf, &desoutbuf, CLIENT, &eblock);
2099 	/* cleanup */
2100 	krb5_free_cred_contents(bsd_context, &creds);
2101 	krb5_free_creds(bsd_context, new_creds);
2102 	krb5_xfree(msg.data);
2103 }
2104 
2105 
2106 static void
2107 try_normal_rcp(int cur_argc, char **cur_argv)
2108 {
2109 	char *target;
2110 
2111 	/*
2112 	 * Reset all KRB5 relevant flags and set the
2113 	 * cmd-buffer so that normal rcp works
2114 	 */
2115 	krb5auth_flag = encrypt_flag = encrypt_done = 0;
2116 	cmd = cmd_orig;
2117 	cmd_sunw = cmd_sunw_orig;
2118 
2119 	if (cur_argc < 2)
2120 		usage();
2121 
2122 	if (cur_argc > 2)
2123 		targetshouldbedirectory = 1;
2124 
2125 	rem = -1;
2126 
2127 	prev_argc = cur_argc;
2128 	prev_argv = save_argv(cur_argc, cur_argv);
2129 
2130 	(void) init_service(krb5auth_flag);
2131 
2132 	if (target = colon(cur_argv[cur_argc - 1])) {
2133 		toremote(target, cur_argc, cur_argv);
2134 	} else {
2135 		tolocal(cur_argc, cur_argv);
2136 		if (targetshouldbedirectory)
2137 			verifydir(cur_argv[cur_argc - 1]);
2138 	}
2139 	exit(errs);
2140 	/* NOTREACHED */
2141 }
2142 
2143 
2144 static int
2145 init_service(int krb5flag)
2146 {
2147 	struct servent *sp;
2148 	boolean_t success = B_FALSE;
2149 
2150 	if (krb5flag > 0) {
2151 		sp = getservbyname("kshell", "tcp");
2152 		if (sp == NULL) {
2153 			(void) fprintf(stderr,
2154 				gettext("rcp: kshell/tcp: unknown service.\n"
2155 				"trying normal shell/tcp service\n"));
2156 		} else {
2157 			portnumber = sp->s_port;
2158 			success = B_TRUE;
2159 		}
2160 	} else {
2161 		portnumber = htons(IPPORT_CMDSERVER);
2162 		success = B_TRUE;
2163 	}
2164 	return (success);
2165 }
2166 
2167 /*PRINTFLIKE1*/
2168 static void
2169 error(char *fmt, ...)
2170 {
2171 	va_list ap;
2172 	char buf[RCP_BUFSIZE];
2173 	char *cp = buf;
2174 
2175 	va_start(ap, fmt);
2176 	errs++;
2177 	*cp++ = 1;
2178 	(void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap);
2179 	va_end(ap);
2180 
2181 	(void) desrcpwrite(rem, buf, strlen(buf));
2182 	if (iamremote == 0)
2183 		(void) write(2, buf + 1, strlen(buf + 1));
2184 }
2185