xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/rcp.c (revision 3ca4cacd)
1 /*
2  * Copyright 2008 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 		profile_get_options_boolean(bsd_context->profile, realmdef,
384 						option);
385 		/*
386 		 * Check the appdefaults section
387 		 */
388 		profile_get_options_boolean(bsd_context->profile, appdef,
389 						option);
390 		profile_get_options_string(bsd_context->profile, appdef,
391 						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, error_message(status));
645 					} else {
646 						(void) fprintf(stderr,
647 							gettext("trying normal"
648 							" rcp...\n"));
649 					}
650 					/*
651 					 * kcmd() failed, so we have to
652 					 * fallback to normal rcp
653 					 */
654 					try_normal_rcp(prev_argc, prev_argv);
655 				} else {
656 					rem = sock;
657 					session_key = &cred->keyblock;
658 					if (kcmd_proto == KCMD_NEW_PROTOCOL) {
659 						/* CSTYLED */
660 						status = krb5_auth_con_getlocalsubkey(bsd_context, auth_context, &session_key);
661 						if (status) {
662 							com_err("rcp", status,
663 								"determining "
664 								"subkey for "
665 								"session");
666 							exit(1);
667 						}
668 						if (!session_key) {
669 							com_err("rcp", 0,
670 								"no subkey "
671 								"negotiated for"
672 								" connection");
673 							exit(1);
674 						}
675 					}
676 					eblock.crypto_entry =
677 						session_key->enctype;
678 					eblock.key =
679 						(krb5_keyblock *)session_key;
680 
681 					init_encrypt(encrypt_flag,
682 						bsd_context, kcmd_proto,
683 						&desinbuf, &desoutbuf, CLIENT,
684 						&eblock);
685 					if (encrypt_flag > 0) {
686 						char *s = gettext("This rcp "
687 							"session is using "
688 							"encryption for all "
689 							"data transmissions."
690 							"\r\n");
691 
692 						(void) write(2, s, strlen(s));
693 					}
694 				}
695 				if (response() < 0)
696 					exit(1);
697 
698 				} else {
699 
700 				/*
701 				 * ACL support: try to find out if the remote
702 				 * site is running acl cognizant version of
703 				 * rcp. A special binary name is used for this
704 				 * purpose.
705 				 */
706 				aclflag = 1;
707 				acl_aclflag = 1;
708 
709 				/*
710 				 * First see if the remote side will support
711 				 * both aclent_t and ace_t acl's?
712 				 */
713 				(void) snprintf(bp, buffersize, "%s -tZ %s",
714 							cmd_sunw, targ);
715 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
716 					    tuser ? tuser : pwd->pw_name,
717 					    bp, 0, AF_INET6);
718 				if (rem < 0)
719 					exit(1);
720 
721 				/*
722 				 * This is similar to routine response().
723 				 * If response is not ok, treat the other
724 				 * side as non-acl rcp.
725 				 */
726 				if (read(rem, &resp, sizeof (resp))
727 				    != sizeof (resp))
728 					lostconn();
729 				if (resp != 0) {
730 					acl_aclflag = 0;
731 					(void) snprintf(bp, buffersize,
732 					    "%s -t %s", cmd_sunw, targ);
733 
734 					(void) close(rem);
735 					host = thost;
736 					rem = rcmd_af(&host, portnumber,
737 					    pwd->pw_name,
738 					    tuser ? tuser : pwd->pw_name,
739 					    bp, 0, AF_INET6);
740 					if (rem < 0)
741 						exit(1);
742 
743 					if (read(rem, &resp, sizeof (resp))
744 					    != sizeof (resp))
745 						lostconn();
746 					if (resp != 0) {
747 						/*
748 						 * Not OK:
749 						 * The other side is running
750 						 * non-acl rcp. Try again with
751 						 * normal stuff
752 						 */
753 						aclflag = 0;
754 						(void) snprintf(bp, buffersize,
755 						    "%s -t %s", cmd, targ);
756 						(void) close(rem);
757 						host = thost;
758 						rem = rcmd_af(&host, portnumber,
759 						    pwd->pw_name,
760 						    tuser ? tuser :
761 						    pwd->pw_name, bp, 0,
762 						    AF_INET6);
763 						if (rem < 0)
764 							exit(1);
765 						if (response() < 0)
766 						    exit(1);
767 					}
768 				}
769 				/* everything should be fine now */
770 				(void) setuid(userid);
771 
772 				}
773 			}
774 			source(1, argv + i);
775 		}
776 	}
777 }
778 
779 static void
780 tolocal(int argc, char *argv[])
781 {
782 	int i;
783 	char *host, *src, *suser, *lhost;
784 	char resp;
785 	size_t buffersize;
786 	char bp[RCP_BUFSIZE];
787 	krb5_creds *cred;
788 	char *arglist[MAXARGS+1];
789 	buffersize = RCP_BUFSIZE;
790 
791 	for (i = 0; i < argc - 1; i++) {
792 		if (!(src = colon(argv[i]))) {	/* local to local */
793 			(void) addargs(arglist, "cp",
794 			    iamrecursive ? "-r" : "", pflag ? "-p" : "",
795 			    zflag ? "-z" : "", argv[i], argv[argc - 1],
796 			    (char *)NULL);
797 			if (susystem(_PATH_CP, arglist) == -1)
798 				errs++;
799 			continue;
800 		}
801 		*src++ = 0;
802 		if (*src == 0)
803 			src = ".";
804 		host = search_char((unsigned char *)argv[i], '@');
805 		if (host) {
806 			*host++ = 0;
807 			suser = argv[i];
808 			if (*suser == '\0') {
809 				suser = pwd->pw_name;
810 			} else if (!okname(suser)) {
811 				errs++;
812 				continue;
813 			}
814 		} else {
815 			host = argv[i];
816 			suser = pwd->pw_name;
817 		}
818 		host = removebrackets(host);
819 		lhost = host;
820 		if (krb5auth_flag > 0) {
821 
822 		(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
823 		authopts = AP_OPTS_MUTUAL_REQUIRED;
824 		status = kcmd(&sock, &host,
825 				portnumber,
826 				pwd->pw_name, suser,
827 				bp,
828 				0,	/* &rfd2 */
829 				"host",
830 				krb_realm,
831 				bsd_context,
832 				&auth_context,
833 				&cred,
834 				0,	/* No seq # */
835 				0,	/* No server seq # */
836 				authopts,
837 				1,	/* Not any port # */
838 				&kcmd_proto);
839 		if (status) {
840 			/*
841 			 * If new protocol requested, we dont
842 			 * fallback to less secure ones.
843 			 */
844 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
845 				(void) fprintf(stderr, gettext("rcp: kcmdv2 "
846 					"to host %s failed - %s\n"
847 					"Fallback to normal rcp denied."),
848 					host, error_message(status));
849 				exit(1);
850 			}
851 			if (status != -1) {
852 				(void) fprintf(stderr, gettext("rcp: kcmd "
853 						"to host %s failed - %s,\n"
854 						"trying normal rcp...\n\n"),
855 						host, error_message(status));
856 			} else {
857 				(void) fprintf(stderr,
858 					gettext("trying normal rcp...\n"));
859 			}
860 			/*
861 			 * kcmd() failed, so we have to
862 			 * fallback to normal rcp
863 			 */
864 			try_normal_rcp(prev_argc, prev_argv);
865 		} else {
866 			rem = sock;
867 			session_key = &cred->keyblock;
868 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
869 				status = krb5_auth_con_getlocalsubkey(
870 						bsd_context, auth_context,
871 						&session_key);
872 				if (status) {
873 					com_err("rcp", status, "determining "
874 						"subkey for session");
875 					exit(1);
876 				}
877 				if (!session_key) {
878 					com_err("rcp", 0, "no subkey negotiated"
879 						" for connection");
880 					exit(1);
881 				}
882 			}
883 			eblock.crypto_entry = session_key->enctype;
884 			eblock.key = (krb5_keyblock *)session_key;
885 
886 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
887 					&desinbuf, &desoutbuf, CLIENT,
888 					&eblock);
889 			if (encrypt_flag > 0) {
890 				char *s = gettext("This rcp "
891 					"session is using DES "
892 					"encryption for all "
893 					"data transmissions."
894 					"\r\n");
895 
896 				(void) write(2, s, strlen(s));
897 			}
898 		}
899 
900 		}
901 		else
902 		{
903 
904 		/*
905 		 * ACL support: try to find out if the remote site is
906 		 * running acl cognizant version of rcp.
907 		 */
908 		aclflag = 1;
909 		acl_aclflag = 1;
910 
911 		(void) snprintf(bp, buffersize, "%s -Zf %s", cmd_sunw, src);
912 		rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
913 			    bp, 0, AF_INET6);
914 
915 		if (rem < 0) {
916 			++errs;
917 			continue;
918 		}
919 
920 		/*
921 		 * The remote system is supposed to send an ok response.
922 		 * If there are any data other than "ok", it must be error
923 		 * messages from the remote system. We can assume the
924 		 * remote system is running non-acl version rcp.
925 		 */
926 		if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
927 			lostconn();
928 		if (resp != 0) {
929 
930 			/*
931 			 * Try again without ace_acl support
932 			 */
933 			acl_aclflag = 0;
934 			(void) snprintf(bp, buffersize, "%s -f %s",
935 			    cmd_sunw, src);
936 			rem = rcmd_af(&host, portnumber, pwd->pw_name, suser,
937 			    bp, 0, AF_INET6);
938 
939 			if (rem < 0) {
940 				++errs;
941 				continue;
942 			}
943 
944 			if (read(rem, &resp, sizeof (resp)) != sizeof (resp))
945 				lostconn();
946 
947 			/*
948 			 * NOT ok:
949 			 * The other side is running non-acl rcp.
950 			 * Try again with normal stuff
951 			 */
952 			aclflag = 0;
953 			(void) snprintf(bp, buffersize, "%s -f %s", cmd, src);
954 				(void) close(rem);
955 				host = lhost;
956 				rem = rcmd_af(&host, portnumber, pwd->pw_name,
957 						suser, bp, 0, AF_INET6);
958 			if (rem < 0) {
959 				++errs;
960 				continue;
961 			}
962 		}
963 		}
964 
965 		sink(1, argv + argc - 1);
966 
967 		(void) close(rem);
968 		rem = -1;
969 	}
970 }
971 
972 
973 static void
974 verifydir(char *cp)
975 {
976 	struct stat stb;
977 
978 	if (stat(cp, &stb) >= 0) {
979 		if ((stb.st_mode & S_IFMT) == S_IFDIR)
980 			return;
981 		errno = ENOTDIR;
982 	}
983 	error("rcp: %s: %s.\n", cp, strerror(errno));
984 	exit(1);
985 }
986 
987 static char *
988 colon(char *cp)
989 {
990 	boolean_t is_bracket_open = B_FALSE;
991 
992 	for (; *cp; ++cp) {
993 		if (*cp == '[')
994 			is_bracket_open = B_TRUE;
995 		else if (*cp == ']')
996 			is_bracket_open = B_FALSE;
997 		else if (*cp == ':' && !is_bracket_open)
998 			return (cp);
999 		else if (*cp == '/')
1000 			return (0);
1001 	}
1002 	return (0);
1003 }
1004 
1005 static int
1006 okname(char *cp0)
1007 {
1008 	register char *cp = cp0;
1009 	register int c;
1010 
1011 	do {
1012 		c = *cp;
1013 		if (c & 0200)
1014 			goto bad;
1015 		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
1016 			goto bad;
1017 	} while (*++cp);
1018 	return (1);
1019 bad:
1020 	(void) fprintf(stderr, "rcp: invalid user name %s\n", cp0);
1021 	return (0);
1022 }
1023 
1024 
1025 static char *
1026 removebrackets(char *str)
1027 {
1028 	char *newstr = str;
1029 
1030 	if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
1031 		newstr = str + 1;
1032 		str[strlen(str) - 1] = '\0';
1033 	}
1034 	return (newstr);
1035 }
1036 
1037 static int
1038 susystem(char *path, char **arglist)
1039 {
1040 	int status, pid, w;
1041 	register void (*istat)(), (*qstat)();
1042 	int pfds[2];
1043 	char buf[BUFSIZ];
1044 	int cnt;
1045 	boolean_t seen_stderr_traffic;
1046 
1047 	/*
1048 	 * Due to the fact that rcp uses rsh to copy between 2 remote
1049 	 * machines, rsh doesn't return the exit status of the remote
1050 	 * command, and we can't modify the rcmd protocol used by rsh
1051 	 * (for interoperability reasons) we use the hack of using any
1052 	 * output on stderr as indication that an error occurred and
1053 	 * that we should return a non-zero error code.
1054 	 */
1055 
1056 	if (pipe(pfds) == -1) {
1057 		(void) fprintf(stderr, "Couldn't create pipe: %s\n",
1058 		    strerror(errno));
1059 		return (-1);
1060 	}
1061 
1062 	if ((pid = vfork()) < 0) {
1063 		(void) close(pfds[0]);
1064 		(void) close(pfds[1]);
1065 		(void) fprintf(stderr, "Couldn't fork child process: %s\n",
1066 		    strerror(errno));
1067 		return (-1);
1068 	} else if (pid == 0) {
1069 		/*
1070 		 * Child.
1071 		 */
1072 		(void) close(pfds[0]);
1073 		/*
1074 		 * Send stderr messages down the pipe so that we can detect
1075 		 * them in the parent process.
1076 		 */
1077 		if (pfds[1] != STDERR_FILENO) {
1078 			(void) dup2(pfds[1], STDERR_FILENO);
1079 			(void) close(pfds[1]);
1080 		}
1081 		/*
1082 		 * This shell does not inherit the additional privilege
1083 		 * we have in our Permitted set.
1084 		 */
1085 		(void) execv(path, arglist);
1086 		_exit(127);
1087 	}
1088 	/*
1089 	 * Parent.
1090 	 */
1091 	istat = signal(SIGINT, SIG_IGN);
1092 	qstat = signal(SIGQUIT, SIG_IGN);
1093 
1094 	(void) close(pfds[1]);
1095 	seen_stderr_traffic = B_FALSE;
1096 	while ((cnt = read(pfds[0], buf, sizeof (buf))) > 0) {
1097 		/*
1098 		 * If any data is read from the pipe the child process
1099 		 * has output something on stderr so we set the boolean
1100 		 * 'seen_stderr_traffic' to true, which will cause the
1101 		 * function to return -1.
1102 		 */
1103 		(void) write(STDERR_FILENO, buf, cnt);
1104 		seen_stderr_traffic = B_TRUE;
1105 	}
1106 	(void) close(pfds[0]);
1107 	while ((w = wait(&status)) != pid && w != -1)
1108 		;
1109 	if (w == -1)
1110 		status = -1;
1111 
1112 	(void) signal(SIGINT, istat);
1113 	(void) signal(SIGQUIT, qstat);
1114 
1115 	return (seen_stderr_traffic ? -1 : status);
1116 }
1117 
1118 static void
1119 source(int argc, char *argv[])
1120 {
1121 	struct stat stb;
1122 	static BUF buffer;
1123 	BUF *bp;
1124 	int x, readerr, f, amt;
1125 	char *last, *name, buf[RCP_BUFSIZE];
1126 	off_t off, size, i;
1127 	ssize_t cnt;
1128 	struct linger lingerbuf;
1129 
1130 	for (x = 0; x < argc; x++) {
1131 		name = argv[x];
1132 		if ((f = open(name, O_RDONLY, 0)) < 0) {
1133 			error("rcp: %s: %s\n", name, strerror(errno));
1134 			continue;
1135 		}
1136 		if (fstat(f, &stb) < 0)
1137 			goto notreg;
1138 		switch (stb.st_mode&S_IFMT) {
1139 
1140 		case S_IFREG:
1141 			break;
1142 
1143 		case S_IFDIR:
1144 			if (iamrecursive) {
1145 				(void) close(f);
1146 				rsource(name, &stb);
1147 				continue;
1148 			}
1149 			/* FALLTHROUGH */
1150 		default:
1151 notreg:
1152 			(void) close(f);
1153 			error("rcp: %s: not a plain file\n", name);
1154 			continue;
1155 		}
1156 		last = rindex(name, '/');
1157 		if (last == 0)
1158 			last = name;
1159 		else
1160 			last++;
1161 		if (pflag) {
1162 			time_t mtime, atime;
1163 			time_t now;
1164 
1165 			/*
1166 			 * Make it compatible with possible future
1167 			 * versions expecting microseconds.
1168 			 */
1169 			mtime = stb.st_mtime;
1170 			atime = stb.st_atime;
1171 
1172 			if ((mtime < 0) || (atime < 0)) {
1173 				now = time(NULL);
1174 
1175 				if (mtime < 0) {
1176 					mtime = now;
1177 					error("negative modification time on "
1178 					    "%s; not preserving\n", name);
1179 				}
1180 				if (atime < 0) {
1181 					atime = now;
1182 					error("negative access time on "
1183 					    "%s; not preserving\n", name);
1184 				}
1185 			}
1186 			(void) snprintf(buf, sizeof (buf), "T%ld 0 %ld 0\n",
1187 							mtime, atime);
1188 			(void) desrcpwrite(rem, buf, strlen(buf));
1189 			if (response() < 0) {
1190 				(void) close(f);
1191 				continue;
1192 			}
1193 		}
1194 		(void) snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
1195 			(uint_t)(stb.st_mode & 07777), (longlong_t)stb.st_size,
1196 			last);
1197 		(void) desrcpwrite(rem, buf, strlen(buf));
1198 		if (response() < 0) {
1199 			(void) close(f);
1200 			continue;
1201 		}
1202 
1203 		/* ACL support: send */
1204 		if (aclflag | acl_aclflag) {
1205 			/* get acl from f and send it over */
1206 			if (sendacl(f) == ACL_FAIL) {
1207 				(void) close(f);
1208 				continue;
1209 			}
1210 		}
1211 		if ((krb5auth_flag > 0) || (iamremote == 1)) {
1212 			bp = allocbuf(&buffer, f, RCP_BUFSIZE);
1213 			if (bp == NULLBUF) {
1214 				(void) close(f);
1215 				continue;
1216 			}
1217 			readerr = 0;
1218 			for (i = 0; i < stb.st_size; i += bp->cnt) {
1219 				amt = bp->cnt;
1220 				if (i + amt > stb.st_size)
1221 					amt = stb.st_size - i;
1222 				if (readerr == 0 &&
1223 				    read(f, bp->buf, amt) != amt)
1224 					readerr = errno;
1225 				(void) desrcpwrite(rem, bp->buf, amt);
1226 			}
1227 			(void) close(f);
1228 			if (readerr == 0)
1229 				(void) desrcpwrite(rem, "", 1);
1230 			else
1231 				error("rcp: %s: %s\n", name,
1232 				    error_message(readerr));
1233 		} else {
1234 			cnt = off = 0;
1235 			size = stb.st_size;
1236 			while (size != 0) {
1237 				amt = MIN(size, SENDFILE_SIZE);
1238 				cnt = sendfile(rem, f, &off, amt);
1239 				if (cnt == -1) {
1240 					if (errno == EINTR) {
1241 						continue;
1242 					} else {
1243 						break;
1244 					}
1245 				}
1246 				if (cnt == 0)
1247 					break;
1248 				size -= cnt;
1249 			}
1250 			if (cnt < 0) {
1251 				error("rcp: %s: %s\n", name, strerror(errno));
1252 			} else if (cnt == 0 && size != 0) {
1253 				error("rcp: %s: unexpected end of file\n",
1254 					name);
1255 				lingerbuf.l_onoff = 1;
1256 				lingerbuf.l_linger = 0;
1257 				(void) setsockopt(rem, SOL_SOCKET, SO_LINGER,
1258 					&lingerbuf, sizeof (lingerbuf));
1259 				/*
1260 				 * When response() (see below) is invoked it
1261 				 * tries to read data from closed handle which
1262 				 * triggers error and lostconn() function.
1263 				 * lostconn() terminates the program with
1264 				 * appropriate message.
1265 				 */
1266 				(void) close(rem);
1267 				rem = -1;
1268 			} else {
1269 				(void) write(rem, "", 1);
1270 			}
1271 			(void) close(f);
1272 		}
1273 		(void) response();
1274 	}
1275 }
1276 
1277 
1278 static void
1279 rsource(char *name, struct stat *statp)
1280 {
1281 	DIR *d;
1282 	struct dirent *dp;
1283 	char *last, *vect[1];
1284 	char path[MAXPATHLEN];
1285 
1286 	if (!(d = opendir(name))) {
1287 		error("rcp: %s: %s\n", name, strerror(errno));
1288 		return;
1289 	}
1290 	last = rindex(name, '/');
1291 	if (last == 0)
1292 		last = name;
1293 	else
1294 		last++;
1295 	if (pflag) {
1296 		(void) snprintf(path, sizeof (path), "T%ld 0 %ld 0\n",
1297 				statp->st_mtime, statp->st_atime);
1298 		(void) desrcpwrite(rem, path, strlen(path));
1299 		if (response() < 0) {
1300 			(void) closedir(d);
1301 			return;
1302 		}
1303 	}
1304 	(void) snprintf(path, sizeof (path), "D%04o %d %s\n",
1305 	    (uint_t)(statp->st_mode & 07777), 0, last);
1306 	(void) desrcpwrite(rem, path, strlen(path));
1307 
1308 	/* acl support for directory */
1309 	if (aclflag) {
1310 		/* get acl from f and send it over */
1311 		if (sendacl(d->dd_fd) == ACL_FAIL) {
1312 			(void) closedir(d);
1313 			return;
1314 		}
1315 	}
1316 
1317 	if (response() < 0) {
1318 		(void) closedir(d);
1319 		return;
1320 	}
1321 
1322 	while (dp = readdir(d)) {
1323 		if (dp->d_ino == 0)
1324 			continue;
1325 		if ((strcmp(dp->d_name, ".") == 0) ||
1326 		    (strcmp(dp->d_name, "..") == 0))
1327 			continue;
1328 		if ((uint_t)strlen(name) + 1 + strlen(dp->d_name) >=
1329 			MAXPATHLEN - 1) {
1330 			error("%s/%s: name too long.\n", name, dp->d_name);
1331 			continue;
1332 		}
1333 		(void) snprintf(path, sizeof (path), "%s/%s",
1334 					name, dp->d_name);
1335 		vect[0] = path;
1336 		source(1, vect);
1337 	}
1338 	(void) closedir(d);
1339 	(void) desrcpwrite(rem, "E\n", 2);
1340 	(void) response();
1341 }
1342 
1343 static int
1344 response(void)
1345 {
1346 	register char *cp;
1347 	char ch, resp, rbuf[RCP_BUFSIZE];
1348 
1349 	if (desrcpread(rem, &resp, 1) != 1)
1350 		lostconn();
1351 	cp = rbuf;
1352 	switch (resp) {
1353 	case 0:				/* ok */
1354 		return (0);
1355 	default:
1356 		*cp++ = resp;
1357 		/* FALLTHROUGH */
1358 	case 1:				/* error, followed by err msg */
1359 	case 2:				/* fatal error, "" */
1360 		do {
1361 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1362 				lostconn();
1363 			*cp++ = ch;
1364 		} while (cp < &rbuf[RCP_BUFSIZE] && ch != '\n');
1365 
1366 		if (!iamremote)
1367 			(void) write(STDERR_FILENO, rbuf, cp - rbuf);
1368 		++errs;
1369 		if (resp == 1)
1370 			return (-1);
1371 		exit(1);
1372 	}
1373 	/*NOTREACHED*/
1374 }
1375 
1376 static void
1377 lostconn(void)
1378 {
1379 	if (!iamremote)
1380 		(void) fprintf(stderr, "rcp: lost connection\n");
1381 	exit(1);
1382 }
1383 
1384 
1385 static void
1386 sink(int argc, char *argv[])
1387 {
1388 	char *cp;
1389 	static BUF buffer;
1390 	struct stat stb;
1391 	struct timeval tv[2];
1392 	BUF *bp;
1393 	off_t i, j;
1394 	char ch, *targ, *why;
1395 	int amt, count, exists, first, mask, mode;
1396 	off_t size;
1397 	int ofd, setimes, targisdir, wrerr;
1398 	char *np, *vect[1], buf[RCP_BUFSIZE];
1399 	char *namebuf = NULL;
1400 	size_t namebuf_sz = 0;
1401 	size_t need;
1402 
1403 #define	atime	tv[0]
1404 #define	mtime	tv[1]
1405 #define	SCREWUP(str)	{ why = str; goto screwup; }
1406 
1407 	setimes = targisdir = 0;
1408 	mask = umask(0);
1409 	if (!pflag)
1410 		(void) umask(mask);
1411 	if (argc != 1) {
1412 		error("rcp: ambiguous target\n");
1413 		exit(1);
1414 	}
1415 	targ = *argv;
1416 	if (targetshouldbedirectory)
1417 		verifydir(targ);
1418 	(void) desrcpwrite(rem, "", 1);
1419 
1420 	if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
1421 		targisdir = 1;
1422 	for (first = 1; ; first = 0) {
1423 		cp = buf;
1424 		if (desrcpread(rem, cp, 1) <= 0) {
1425 			if (namebuf != NULL)
1426 				free(namebuf);
1427 			return;
1428 		}
1429 
1430 		if (*cp++ == '\n')
1431 			SCREWUP("unexpected <newline>");
1432 		do {
1433 			if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch))
1434 				SCREWUP("lost connection");
1435 			*cp++ = ch;
1436 		} while (cp < &buf[RCP_BUFSIZE - 1] && ch != '\n');
1437 		*cp = 0;
1438 
1439 		if (buf[0] == '\01' || buf[0] == '\02') {
1440 			if (iamremote == 0)
1441 				(void) write(STDERR_FILENO, buf + 1,
1442 				    strlen(buf + 1));
1443 			if (buf[0] == '\02')
1444 				exit(1);
1445 			errs++;
1446 			continue;
1447 		}
1448 		if (buf[0] == 'E') {
1449 			(void) desrcpwrite(rem, "", 1);
1450 			if (namebuf != NULL)
1451 				free(namebuf);
1452 			return;
1453 		}
1454 
1455 		if (ch == '\n')
1456 			*--cp = 0;
1457 		cp = buf;
1458 		if (*cp == 'T') {
1459 			setimes++;
1460 			cp++;
1461 			mtime.tv_sec = strtol(cp, &cp, 0);
1462 			if (*cp++ != ' ')
1463 				SCREWUP("mtime.sec not delimited");
1464 			mtime.tv_usec = strtol(cp, &cp, 0);
1465 			if (*cp++ != ' ')
1466 				SCREWUP("mtime.usec not delimited");
1467 			atime.tv_sec = strtol(cp, &cp, 0);
1468 			if (*cp++ != ' ')
1469 				SCREWUP("atime.sec not delimited");
1470 			atime.tv_usec = strtol(cp, &cp, 0);
1471 			if (*cp++ != '\0')
1472 				SCREWUP("atime.usec not delimited");
1473 			(void) desrcpwrite(rem, "", 1);
1474 			continue;
1475 		}
1476 		if (*cp != 'C' && *cp != 'D') {
1477 			/*
1478 			 * Check for the case "rcp remote:foo\* local:bar".
1479 			 * In this case, the line "No match." can be returned
1480 			 * by the shell before the rcp command on the remote is
1481 			 * executed so the ^Aerror_message convention isn't
1482 			 * followed.
1483 			 */
1484 			if (first) {
1485 				error("%s\n", cp);
1486 				exit(1);
1487 			}
1488 			SCREWUP("expected control record");
1489 		}
1490 		mode = 0;
1491 		for (++cp; cp < buf + 5; cp++) {
1492 			if (*cp < '0' || *cp > '7')
1493 				SCREWUP("bad mode");
1494 			mode = (mode << 3) | (*cp - '0');
1495 		}
1496 		if (*cp++ != ' ')
1497 			SCREWUP("mode not delimited");
1498 		size = 0;
1499 		while (isdigit(*cp))
1500 			size = size * 10 + (*cp++ - '0');
1501 		if (*cp++ != ' ')
1502 			SCREWUP("size not delimited");
1503 		if (targisdir) {
1504 			need = strlen(targ) + sizeof ("/") + strlen(cp);
1505 			if (need > namebuf_sz) {
1506 			    if ((namebuf = realloc(namebuf, need)) == NULL) {
1507 					error("rcp: out of memory\n");
1508 					exit(1);
1509 			    }
1510 			    namebuf_sz = need;
1511 			}
1512 			(void) snprintf(namebuf, need, "%s%s%s", targ,
1513 			    *targ ? "/" : "", cp);
1514 			np = namebuf;
1515 		} else {
1516 			np = targ;
1517 		}
1518 
1519 		exists = stat(np, &stb) == 0;
1520 		if (buf[0] == 'D') {
1521 			if (exists) {
1522 				if ((stb.st_mode&S_IFMT) != S_IFDIR) {
1523 					if (aclflag | acl_aclflag) {
1524 						/*
1525 						 * consume acl in the pipe
1526 						 * fd = -1 to indicate the
1527 						 * special case
1528 						 */
1529 						if (recvacl(-1, exists, pflag)
1530 						    == ACL_FAIL) {
1531 							goto bad;
1532 						}
1533 					}
1534 					errno = ENOTDIR;
1535 					goto bad;
1536 				}
1537 				if (pflag)
1538 					(void) chmod(np, mode);
1539 			} else if (mkdir(np, mode) < 0) {
1540 				if (aclflag) {
1541 					/* consume acl in the pipe */
1542 					(void) recvacl(-1, exists, pflag);
1543 				}
1544 				goto bad;
1545 			}
1546 
1547 			/* acl support for directories */
1548 			if (aclflag | acl_aclflag) {
1549 				int dfd;
1550 
1551 				if ((dfd = open(np, O_RDONLY)) == -1)
1552 					goto bad;
1553 
1554 				/* get acl and set it to ofd */
1555 				if (recvacl(dfd, exists, pflag) == ACL_FAIL) {
1556 					(void) close(dfd);
1557 					if (!exists)
1558 						(void) rmdir(np);
1559 					goto bad;
1560 				}
1561 				(void) close(dfd);
1562 			}
1563 
1564 			vect[0] = np;
1565 			sink(1, vect);
1566 			if (setimes) {
1567 				setimes = 0;
1568 				if (utimes(np, tv) < 0)
1569 				    error("rcp: can't set times on %s: %s\n",
1570 					np, strerror(errno));
1571 			}
1572 			continue;
1573 		}
1574 
1575 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
1576 bad:
1577 			error("rcp: %s: %s\n", np, strerror(errno));
1578 			continue;
1579 		}
1580 
1581 		/*
1582 		 * If the output file exists we have to force zflag off
1583 		 * to avoid erroneously seeking past old data.
1584 		 */
1585 		zopen(ofd, zflag && !exists);
1586 
1587 		if (exists && pflag)
1588 			(void) fchmod(ofd, mode);
1589 
1590 		(void) desrcpwrite(rem, "", 1);
1591 
1592 		/*
1593 		 * ACL support: receiving
1594 		 */
1595 		if (aclflag | acl_aclflag) {
1596 			/* get acl and set it to ofd */
1597 			if (recvacl(ofd, exists, pflag) == ACL_FAIL) {
1598 				(void) close(ofd);
1599 				if (!exists)
1600 					(void) unlink(np);
1601 				continue;
1602 			}
1603 		}
1604 
1605 		if ((bp = allocbuf(&buffer, ofd, RCP_BUFSIZE)) == 0) {
1606 			(void) close(ofd);
1607 			continue;
1608 		}
1609 		cp = bp->buf;
1610 		count = 0;
1611 		wrerr = 0;
1612 		for (i = 0; i < size; i += RCP_BUFSIZE) {
1613 			amt = RCP_BUFSIZE;
1614 			if (i + amt > size)
1615 				amt = size - i;
1616 			count += amt;
1617 			do {
1618 				j = desrcpread(rem, cp, amt);
1619 				if (j <= 0) {
1620 					int sverrno = errno;
1621 
1622 					/*
1623 					 * Connection to supplier lost.
1624 					 * Truncate file to correspond
1625 					 * to amount already transferred.
1626 					 *
1627 					 * Note that we must call ftruncate()
1628 					 * before any call to error() (which
1629 					 * might result in a SIGPIPE and
1630 					 * sudden death before we have a chance
1631 					 * to correct the file's size).
1632 					 */
1633 					size = lseek(ofd, 0, SEEK_CUR);
1634 					if ((ftruncate(ofd, size)  == -1) &&
1635 					    (errno != EINVAL) &&
1636 					    (errno != EACCES))
1637 #define		TRUNCERR	"rcp: can't truncate %s: %s\n"
1638 						error(TRUNCERR, np,
1639 						    strerror(errno));
1640 					error("rcp: %s\n",
1641 					    j ? strerror(sverrno) :
1642 					    "dropped connection");
1643 					(void) close(ofd);
1644 					exit(1);
1645 				}
1646 				amt -= j;
1647 				cp += j;
1648 			} while (amt > 0);
1649 			if (count == bp->cnt) {
1650 				cp = bp->buf;
1651 				if (wrerr == 0 &&
1652 				    zwrite(ofd, cp, count) < 0)
1653 					wrerr++;
1654 				count = 0;
1655 			}
1656 		}
1657 		if (count != 0 && wrerr == 0 &&
1658 		    zwrite(ofd, bp->buf, count) < 0)
1659 			wrerr++;
1660 		if (zclose(ofd) < 0)
1661 			wrerr++;
1662 
1663 
1664 		if ((ftruncate(ofd, size)  == -1) && (errno != EINVAL) &&
1665 		    (errno != EACCES)) {
1666 			error(TRUNCERR, np, strerror(errno));
1667 		}
1668 		(void) close(ofd);
1669 		(void) response();
1670 		if (setimes) {
1671 			setimes = 0;
1672 			if (utimes(np, tv) < 0)
1673 				error("rcp: can't set times on %s: %s\n",
1674 				    np, strerror(errno));
1675 		}
1676 		if (wrerr)
1677 			error("rcp: %s: %s\n", np, strerror(errno));
1678 		else
1679 			(void) desrcpwrite(rem, "", 1);
1680 	}
1681 screwup:
1682 	error("rcp: protocol screwup: %s\n", why);
1683 	exit(1);
1684 }
1685 
1686 #ifndef roundup
1687 #define	roundup(x, y)   ((((x)+((y)-1))/(y))*(y))
1688 #endif /* !roundup */
1689 
1690 static BUF *
1691 allocbuf(BUF *bp, int fd, int blksize)
1692 {
1693 	struct stat stb;
1694 	int size;
1695 
1696 	if (fstat(fd, &stb) < 0) {
1697 		error("rcp: fstat: %s\n", strerror(errno));
1698 		return (0);
1699 	}
1700 	size = roundup(stb.st_blksize, blksize);
1701 	if (size == 0)
1702 		size = blksize;
1703 	if (bp->cnt < size) {
1704 		if (bp->buf != 0)
1705 			free(bp->buf);
1706 		bp->buf = (char *)malloc((uint_t)size);
1707 		if (!bp->buf) {
1708 			error("rcp: malloc: out of memory\n");
1709 			return (0);
1710 		}
1711 	}
1712 	bp->cnt = size;
1713 	return (bp);
1714 }
1715 
1716 static void
1717 usage(void)
1718 {
1719 	(void) fprintf(stderr, "%s: \t%s\t%s", gettext("Usage"),
1720 		gettext("\trcp [-p] [-a] [-x] [-k realm] [-PN / -PO] "
1721 #ifdef DEBUG
1722 			"[-D port] "
1723 #endif /* DEBUG */
1724 			"f1 f2; or:\n"),
1725 		gettext("\trcp [-r] [-p] [-a] [-x] "
1726 #ifdef DEBUG
1727 			"[-D port] "
1728 #endif /* DEBUG */
1729 			"[-k realm] [-PN / -PO] f1...fn d2\n"));
1730 	exit(1);
1731 }
1732 
1733 
1734 /*
1735  * sparse file support
1736  */
1737 
1738 static off_t zbsize;
1739 static off_t zlastseek;
1740 
1741 /* is it ok to try to create holes? */
1742 static void
1743 zopen(int fd, int flag)
1744 {
1745 	struct stat st;
1746 
1747 	zbsize = 0;
1748 	zlastseek = 0;
1749 
1750 	if (flag &&
1751 		fstat(fd, &st) == 0 &&
1752 		(st.st_mode & S_IFMT) == S_IFREG)
1753 		zbsize = st.st_blksize;
1754 }
1755 
1756 /* write and/or seek */
1757 static int
1758 zwrite(int fd, char *buf, int nbytes)
1759 {
1760 	off_t block = zbsize ? zbsize : nbytes;
1761 
1762 	do {
1763 		if (block > nbytes)
1764 			block = nbytes;
1765 		nbytes -= block;
1766 
1767 		if (!zbsize || notzero(buf, block)) {
1768 			register int n, count = block;
1769 
1770 			do {
1771 				if ((n = write(fd, buf, count)) < 0)
1772 					return (-1);
1773 				buf += n;
1774 			} while ((count -= n) > 0);
1775 			zlastseek = 0;
1776 		} else {
1777 			if (lseek(fd, (off_t)block, SEEK_CUR) < 0)
1778 				return (-1);
1779 			buf += block;
1780 			zlastseek = 1;
1781 		}
1782 	} while (nbytes > 0);
1783 
1784 	return (0);
1785 }
1786 
1787 /* write last byte of file if necessary */
1788 static int
1789 zclose(int fd)
1790 {
1791 	zbsize = 0;
1792 
1793 	if (zlastseek && (lseek(fd, (off_t)-1, SEEK_CUR) < 0 ||
1794 		zwrite(fd, "", 1) < 0))
1795 		return (-1);
1796 	else
1797 		return (0);
1798 }
1799 
1800 /* return true if buffer is not all zeros */
1801 static int
1802 notzero(char *p, int n)
1803 {
1804 	register int result = 0;
1805 
1806 	while ((int)p & 3 && --n >= 0)
1807 		result |= *p++;
1808 
1809 	while ((n -= 4 * sizeof (int)) >= 0) {
1810 		/* LINTED */
1811 		result |= ((int *)p)[0];
1812 		/* LINTED */
1813 		result |= ((int *)p)[1];
1814 		/* LINTED */
1815 		result |= ((int *)p)[2];
1816 		/* LINTED */
1817 		result |= ((int *)p)[3];
1818 		if (result)
1819 			return (result);
1820 		p += 4 * sizeof (int);
1821 	}
1822 	n += 4 * sizeof (int);
1823 
1824 	while (--n >= 0)
1825 		result |= *p++;
1826 
1827 	return (result);
1828 }
1829 
1830 /*
1831  * New functions to support ACLs
1832  */
1833 
1834 /*
1835  * Get acl from f and send it over.
1836  * ACL record includes acl entry count, acl text length, and acl text.
1837  */
1838 static int
1839 sendacl(int f)
1840 {
1841 	int		aclcnt;
1842 	char		*acltext;
1843 	char		buf[BUFSIZ];
1844 	acl_t		*aclp;
1845 	char		acltype;
1846 	int		aclerror;
1847 	int		trivial;
1848 
1849 
1850 	aclerror = facl_get(f, ACL_NO_TRIVIAL, &aclp);
1851 	if (aclerror != 0) {
1852 		error("can't retrieve ACL: %s \n", acl_strerror(aclerror));
1853 		return (ACL_FAIL);
1854 	}
1855 
1856 	/*
1857 	 * if acl type is not ACLENT_T and were operating in acl_aclflag == 0
1858 	 * then don't do the malloc and facl(fd, getcntcmd,...);
1859 	 * since the remote side doesn't support alternate style ACL's.
1860 	 */
1861 
1862 	if (aclp && (acl_type(aclp) != ACLENT_T) && (acl_aclflag == 0)) {
1863 		aclcnt = MIN_ACL_ENTRIES;
1864 		acltype = 'A';
1865 		trivial = ACL_IS_TRIVIAL;
1866 	} else {
1867 
1868 		aclcnt = (aclp != NULL) ? acl_cnt(aclp) : 0;
1869 
1870 		if (aclp) {
1871 			acltype = (acl_type(aclp) != ACLENT_T) ? 'Z' : 'A';
1872 			aclcnt = acl_cnt(aclp);
1873 			trivial = (acl_flags(aclp) & ACL_IS_TRIVIAL);
1874 		} else {
1875 			acltype = 'A';
1876 			aclcnt = MIN_ACL_ENTRIES;
1877 			trivial = ACL_IS_TRIVIAL;
1878 		}
1879 
1880 	}
1881 
1882 	/* send the acl count over */
1883 	(void) snprintf(buf, sizeof (buf), "%c%d\n", acltype, aclcnt);
1884 	(void) desrcpwrite(rem, buf, strlen(buf));
1885 
1886 	/*
1887 	 * only send acl when we have an aclp, which would
1888 	 * imply its not trivial.
1889 	 */
1890 	if (aclp && (trivial != ACL_IS_TRIVIAL)) {
1891 		acltext = acl_totext(aclp, 0);
1892 		if (acltext == NULL) {
1893 			error("rcp: failed to convert to text\n");
1894 			acl_free(aclp);
1895 			return (ACL_FAIL);
1896 		}
1897 
1898 		/* send ACLs over: send the length first */
1899 		(void) snprintf(buf, sizeof (buf), "%c%d\n",
1900 		    acltype, strlen(acltext));
1901 
1902 		(void) desrcpwrite(rem, buf, strlen(buf));
1903 		(void) desrcpwrite(rem, acltext, strlen(acltext));
1904 		free(acltext);
1905 		if (response() < 0) {
1906 			acl_free(aclp);
1907 			return (ACL_FAIL);
1908 		}
1909 
1910 	}
1911 
1912 	if (aclp)
1913 		acl_free(aclp);
1914 	return (ACL_OK);
1915 }
1916 
1917 /*
1918  * Use this routine to get acl entry count and acl text size (in bytes)
1919  */
1920 static int
1921 getaclinfo(int *cnt, int *acltype)
1922 {
1923 	char		buf[BUFSIZ];
1924 	char		*cp;
1925 	char		ch;
1926 
1927 	/* get acl count */
1928 	cp = buf;
1929 	if (desrcpread(rem, cp, 1) <= 0)
1930 		return (ACL_FAIL);
1931 
1932 	switch (*cp++) {
1933 	case 'A':
1934 		*acltype = 0;
1935 		break;
1936 	case 'Z':
1937 		*acltype = 1;
1938 		break;
1939 	default:
1940 		error("rcp: expect an ACL record, but got %c\n", *cp);
1941 		return (ACL_FAIL);
1942 	}
1943 	do {
1944 		if (desrcpread(rem, &ch, sizeof (ch)) != sizeof (ch)) {
1945 			error("rcp: lost connection ..\n");
1946 			return (ACL_FAIL);
1947 		}
1948 		*cp++ = ch;
1949 	} while (cp < &buf[BUFSIZ - 1] && ch != '\n');
1950 	if (ch != '\n') {
1951 		error("rcp: ACL record corrupted \n");
1952 		return (ACL_FAIL);
1953 	}
1954 	cp = &buf[1];
1955 	*cnt = strtol(cp, &cp, 0);
1956 	if (*cp != '\n') {
1957 		error("rcp: ACL record corrupted \n");
1958 		return (ACL_FAIL);
1959 	}
1960 	return (ACL_OK);
1961 }
1962 
1963 
1964 /*
1965  * Receive acl from the pipe and set it to f
1966  */
1967 static int
1968 recvacl(int f, int exists, int preserve)
1969 {
1970 	int		aclcnt;		/* acl entry count */
1971 	int		aclsize;	/* acl text length */
1972 	int		j;
1973 	char		*tp;
1974 	char		*acltext;	/* external format */
1975 	acl_t		*aclp;
1976 	int		acltype;
1977 	int		min_entries;
1978 	int		aclerror;
1979 
1980 	/* get acl count */
1981 	if (getaclinfo(&aclcnt, &acltype) != ACL_OK)
1982 		return (ACL_FAIL);
1983 
1984 	if (acltype == 0) {
1985 		min_entries = MIN_ACL_ENTRIES;
1986 	} else {
1987 		min_entries = 1;
1988 	}
1989 
1990 	if (aclcnt > min_entries) {
1991 		/* get acl text size */
1992 		if (getaclinfo(&aclsize, &acltype) != ACL_OK)
1993 			return (ACL_FAIL);
1994 		if ((acltext = malloc(aclsize + 1)) == NULL) {
1995 			error("rcp: cant allocate memory: %d\n", aclsize);
1996 			return (ACL_FAIL);
1997 		}
1998 
1999 		tp = acltext;
2000 		do {
2001 			j = desrcpread(rem, tp, aclsize);
2002 			if (j <= 0) {
2003 				error("rcp: %s\n", j ? strerror(errno) :
2004 				    "dropped connection");
2005 				exit(1);
2006 			}
2007 			aclsize -= j;
2008 			tp += j;
2009 		} while (aclsize > 0);
2010 		*tp = '\0';
2011 
2012 		if (preserve || !exists) {
2013 			aclerror = acl_fromtext(acltext, &aclp);
2014 			if (aclerror != 0) {
2015 				error("rcp: failed to parse acl : %s\n",
2016 				    acl_strerror(aclerror));
2017 				free(acltext);
2018 				return (ACL_FAIL);
2019 			}
2020 
2021 			if (f != -1) {
2022 				if (facl_set(f, aclp) < 0) {
2023 					error("rcp: failed to set acl\n");
2024 					acl_free(aclp);
2025 					free(acltext);
2026 					return (ACL_FAIL);
2027 				}
2028 			}
2029 			/* -1 means that just consume the data in the pipe */
2030 			acl_free(aclp);
2031 		}
2032 		free(acltext);
2033 		(void) desrcpwrite(rem, "", 1);
2034 	}
2035 	return (ACL_OK);
2036 }
2037 
2038 
2039 static char *
2040 search_char(unsigned char *cp, unsigned char chr)
2041 {
2042 	int	len;
2043 
2044 	while (*cp) {
2045 		if (*cp == chr)
2046 			return ((char *)cp);
2047 		if ((len = mblen((char *)cp, MB_CUR_MAX)) <= 0)
2048 			len = 1;
2049 		cp += len;
2050 	}
2051 	return (0);
2052 }
2053 
2054 
2055 static int
2056 desrcpread(int fd, char *buf, int len)
2057 {
2058 	return ((int)desread(fd, buf, len, 0));
2059 }
2060 
2061 static int
2062 desrcpwrite(int fd, char *buf, int len)
2063 {
2064 	/*
2065 	 * Note that rcp depends on the same file descriptor being both
2066 	 * input and output to the remote side.  This is bogus, especially
2067 	 * when rcp is being run by a rsh that pipes. Fix it here because
2068 	 * it would require significantly more work in other places.
2069 	 * --hartmans 1/96
2070 	 */
2071 
2072 	if (fd == 0)
2073 		fd = 1;
2074 	return ((int)deswrite(fd, buf, len, 0));
2075 }
2076 
2077 static char **
2078 save_argv(int argc, char **argv)
2079 {
2080 	int i;
2081 
2082 	char **local_argv = (char **)calloc((unsigned)argc + 1,
2083 	    (unsigned)sizeof (char *));
2084 
2085 	/*
2086 	 * allocate an extra pointer, so that it is initialized to NULL and
2087 	 * execv() will work
2088 	 */
2089 	for (i = 0; i < argc; i++) {
2090 		local_argv[i] = strsave(argv[i]);
2091 	}
2092 
2093 	return (local_argv);
2094 }
2095 
2096 #define	SIZEOF_INADDR sizeof (struct in_addr)
2097 
2098 static void
2099 answer_auth(char *config_file, char *ccache_file)
2100 {
2101 	krb5_data pname_data, msg;
2102 	krb5_creds creds, *new_creds;
2103 	krb5_ccache cc;
2104 	krb5_auth_context auth_context = NULL;
2105 
2106 	if (config_file) {
2107 		const char *filenames[2];
2108 
2109 		filenames[1] = NULL;
2110 		filenames[0] = config_file;
2111 		if (krb5_set_config_files(bsd_context, filenames))
2112 			exit(1);
2113 	}
2114 	(void) memset((char *)&creds, 0, sizeof (creds));
2115 
2116 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem, &pname_data))
2117 		exit(1);
2118 
2119 	if (krb5_read_message(bsd_context, (krb5_pointer) &rem,
2120 	    &creds.second_ticket))
2121 		exit(1);
2122 
2123 	if (ccache_file == NULL) {
2124 		if (krb5_cc_default(bsd_context, &cc))
2125 			exit(1);
2126 	} else {
2127 		if (krb5_cc_resolve(bsd_context, ccache_file, &cc))
2128 			exit(1);
2129 	}
2130 
2131 	if (krb5_cc_get_principal(bsd_context, cc, &creds.client))
2132 		exit(1);
2133 
2134 	if (krb5_parse_name(bsd_context, pname_data.data, &creds.server))
2135 		exit(1);
2136 
2137 	krb5_xfree(pname_data.data);
2138 	if (krb5_get_credentials(bsd_context, KRB5_GC_USER_USER, cc, &creds,
2139 	    &new_creds))
2140 		exit(1);
2141 
2142 	if (krb5_mk_req_extended(bsd_context, &auth_context,
2143 	    AP_OPTS_USE_SESSION_KEY, NULL, new_creds, &msg))
2144 		exit(1);
2145 
2146 	if (krb5_write_message(bsd_context, (krb5_pointer) & rem, &msg)) {
2147 		krb5_xfree(msg.data);
2148 		exit(1);
2149 	}
2150 	/* setup eblock for des_read and write */
2151 	krb5_copy_keyblock(bsd_context, &new_creds->keyblock, &session_key);
2152 
2153 	/* OK process key */
2154 	eblock.crypto_entry = session_key->enctype;
2155 	eblock.key = (krb5_keyblock *)session_key;
2156 
2157 	init_encrypt(encrypt_flag, bsd_context, KCMD_OLD_PROTOCOL,
2158 	    &desinbuf, &desoutbuf, CLIENT, &eblock);
2159 	/* cleanup */
2160 	krb5_free_cred_contents(bsd_context, &creds);
2161 	krb5_free_creds(bsd_context, new_creds);
2162 	krb5_xfree(msg.data);
2163 }
2164 
2165 
2166 static void
2167 try_normal_rcp(int cur_argc, char **cur_argv)
2168 {
2169 	char *target;
2170 
2171 	/*
2172 	 * Reset all KRB5 relevant flags and set the
2173 	 * cmd-buffer so that normal rcp works
2174 	 */
2175 	krb5auth_flag = encrypt_flag = encrypt_done = 0;
2176 	cmd = cmd_orig;
2177 	cmd_sunw = cmd_sunw_orig;
2178 
2179 	if (cur_argc < 2)
2180 		usage();
2181 
2182 	if (cur_argc > 2)
2183 		targetshouldbedirectory = 1;
2184 
2185 	rem = -1;
2186 
2187 	prev_argc = cur_argc;
2188 	prev_argv = save_argv(cur_argc, cur_argv);
2189 
2190 	(void) init_service(krb5auth_flag);
2191 
2192 	if (target = colon(cur_argv[cur_argc - 1])) {
2193 		toremote(target, cur_argc, cur_argv);
2194 	} else {
2195 		tolocal(cur_argc, cur_argv);
2196 		if (targetshouldbedirectory)
2197 			verifydir(cur_argv[cur_argc - 1]);
2198 	}
2199 	exit(errs);
2200 	/* NOTREACHED */
2201 }
2202 
2203 
2204 static int
2205 init_service(int krb5flag)
2206 {
2207 	struct servent *sp;
2208 	boolean_t success = B_FALSE;
2209 
2210 	if (krb5flag > 0) {
2211 		sp = getservbyname("kshell", "tcp");
2212 		if (sp == NULL) {
2213 			(void) fprintf(stderr,
2214 				gettext("rcp: kshell/tcp: unknown service.\n"
2215 				"trying normal shell/tcp service\n"));
2216 		} else {
2217 			portnumber = sp->s_port;
2218 			success = B_TRUE;
2219 		}
2220 	} else {
2221 		portnumber = htons(IPPORT_CMDSERVER);
2222 		success = B_TRUE;
2223 	}
2224 	return (success);
2225 }
2226 
2227 /*PRINTFLIKE1*/
2228 static void
2229 error(char *fmt, ...)
2230 {
2231 	va_list ap;
2232 	char buf[RCP_BUFSIZE];
2233 	char *cp = buf;
2234 
2235 	va_start(ap, fmt);
2236 	errs++;
2237 	*cp++ = 1;
2238 	(void) vsnprintf(cp, sizeof (buf) - 1, fmt, ap);
2239 	va_end(ap);
2240 
2241 	(void) desrcpwrite(rem, buf, strlen(buf));
2242 	if (iamremote == 0)
2243 		(void) write(2, buf + 1, strlen(buf + 1));
2244 }
2245 
2246 static void
2247 addargs(char **arglist, ...)
2248 {
2249 	va_list ap;
2250 	int i = 0;
2251 	char *pm;
2252 
2253 	va_start(ap, arglist);
2254 	while (i < MAXARGS && (pm = va_arg(ap, char *)) != NULL)
2255 		if (strcmp(pm, ""))
2256 			arglist[i++] = pm;
2257 	arglist[i] = NULL;
2258 	va_end(ap);
2259 }
2260