Home | History | Annotate | Download | only in usr.bin
      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