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