Home | History | Annotate | Download | only in usr.bin
      1 /*
      2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      7 
      8 /*
      9  * Copyright (c) 1983 Regents of the University of California.
     10  * All rights reserved.  The Berkeley software License Agreement
     11  * specifies the terms and conditions for redistribution.
     12  *
     13  */
     14 
     15 #define	_FILE_OFFSET_BITS 64
     16 
     17 #include <sys/time.h>
     18 #include <sys/types.h>
     19 #include <sys/socket.h>
     20 #include <sys/ioctl.h>
     21 /* just for FIONBIO ... */
     22 #include <sys/filio.h>
     23 #include <sys/stat.h>
     24 #include <sys/select.h>
     25 
     26 #include <netinet/in.h>
     27 
     28 #include <assert.h>
     29 #include <fcntl.h>
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <unistd.h>
     33 #include <stdio.h>
     34 #include <errno.h>
     35 #include <signal.h>
     36 #include <pwd.h>
     37 #include <netdb.h>
     38 #include <locale.h>
     39 #include <priv_utils.h>
     40 
     41 #include <k5-int.h>
     42 #include <profile/prof_int.h>
     43 #include <com_err.h>
     44 #include <kcmd.h>
     45 #include <krb5.h>
     46 
     47 /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
     48 typedef void (*sigdisp_t)(int);
     49 
     50 extern errcode_t	profile_get_options_boolean(profile_t, char **,
     51     profile_options_boolean *);
     52 extern errcode_t	profile_get_options_string(profile_t, char **,
     53     profile_option_strings *);
     54 
     55 #define	RSH_BUFSIZ (1024 * 50)
     56 
     57 static char des_inbuf[2 * RSH_BUFSIZ];	/* needs to be > largest read size */
     58 static char des_outbuf[2 * RSH_BUFSIZ];	/* needs to be > largest write size */
     59 static krb5_data desinbuf, desoutbuf;
     60 static krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
     61 static krb5_context bsd_context;
     62 static krb5_auth_context auth_context;
     63 static krb5_creds *cred;
     64 static krb5_keyblock *session_key;
     65 
     66 static int encrypt_flag;	/* Flag set, when encryption is used */
     67 static boolean_t krb5auth_flag;	/* Flag set, when KERBEROS is enabled */
     68 static int fflag;	/* Flag set, if creds to be fwd'ed via -f */
     69 static int Fflag;	/* Flag set, if fwd'able creds to be fwd'ed via -F */
     70 
     71 /* Flag set, if -PN / -PO is specified */
     72 static boolean_t rcmdoption_done;
     73 
     74 /* Flags set, if corres. cmd line options are turned on */
     75 static boolean_t encrypt_done, fwd_done, fwdable_done;
     76 
     77 static profile_options_boolean option[] = {
     78 	{ "encrypt", &encrypt_flag, 0 },
     79 	{ "forward", &fflag, 0 },
     80 	{ "forwardable", &Fflag, 0 },
     81 	{ NULL, NULL, 0 }
     82 };
     83 
     84 static char *rcmdproto;
     85 static profile_option_strings rcmdversion[] = {
     86 	{ "rcmd_protocol", &rcmdproto, 0 },
     87 	{ NULL, NULL, 0 }
     88 };
     89 
     90 static char *realmdef[] = { "realms", NULL, "rsh", NULL };
     91 static char *appdef[] = { "appdefaults", "rsh", NULL };
     92 
     93 static void sendsig(int);
     94 static sigdisp_t sigdisp(int);
     95 static boolean_t init_service(boolean_t);
     96 static int desrshread(int, char *, int);
     97 static int desrshwrite(int, char *, int);
     98 
     99 static int		options;
    100 static int		rfd2;
    101 static int		portnumber;
    102 
    103 static const char	rlogin_path[] = "/usr/bin/rlogin";
    104 static const char	dash_x[] = "-x ";	/* Note the blank after -x */
    105 
    106 static boolean_t readiv, writeiv;
    107 
    108 #define	set2mask(setp)	((setp)->__sigbits[0])
    109 #define	mask2set(mask, setp) \
    110 	((mask) == -1 ? sigfillset(setp) : (set2mask(setp) = (mask)))
    111 
    112 #ifdef DEBUG
    113 #define	DEBUGOPTSTRING	"D:"
    114 #else
    115 #define	DEBUGOPTSTRING	""
    116 #endif	/* DEBUG */
    117 
    118 static void
    119 sigsetmask(int mask)
    120 {
    121 	sigset_t	nset;
    122 
    123 	(void) sigprocmask(0, NULL, &nset);
    124 	mask2set(mask, &nset);
    125 	(void) sigprocmask(SIG_SETMASK, &nset, NULL);
    126 }
    127 
    128 static int
    129 sigblock(int mask)
    130 {
    131 	sigset_t oset;
    132 	sigset_t nset;
    133 
    134 	(void) sigprocmask(0, NULL, &nset);
    135 	mask2set(mask, &nset);
    136 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
    137 	return (set2mask(&oset));
    138 }
    139 
    140 /*
    141  * Get signal disposition (or signal handler) for a given signal
    142  */
    143 static sigdisp_t
    144 sigdisp(int sig)
    145 {
    146 	struct sigaction act;
    147 
    148 	act.sa_handler = NULL;
    149 	act.sa_flags = 0;
    150 	(void) sigemptyset(&act.sa_mask);
    151 	(void) sigaction(sig, NULL, &act);
    152 	return (act.sa_handler);
    153 }
    154 
    155 static pid_t child_pid = -1;
    156 
    157 /*
    158  * If you do a command like "rsh host output | wc"
    159  * and wc terminates, then the parent will receive SIGPIPE
    160  * and the child needs to be terminated.
    161  */
    162 /* ARGSUSED */
    163 static void
    164 sigpipehandler(int signal)
    165 {
    166 	if (child_pid != -1)
    167 		(void) kill(child_pid, SIGKILL);
    168 	exit(EXIT_SUCCESS);
    169 }
    170 
    171 #define	mask(s)	(1 << ((s) - 1))
    172 
    173 static void
    174 usage(void) {
    175 	(void) fprintf(stderr, "%s\n%s\n",
    176 	    gettext("usage: rsh [ -PN / -PO ] [ -l login ] [ -n ] "
    177 		"[ -k realm ] [ -a ] [ -x ] [ -f / -F ] host command"),
    178 	    gettext("       rsh [ -PN / -PO ] [ -l login ] [ -k realm ] "
    179 		"[ -a ] [ -x ] [ -f / -F ] host"));
    180 	exit(EXIT_FAILURE);
    181 }
    182 
    183 static void
    184 die(const char *message)
    185 {
    186 	(void) fputs(message, stderr);
    187 	usage();
    188 }
    189 
    190 static void
    191 usage_forward(void)
    192 {
    193 	die(gettext("rsh: Only one of -f and -F allowed.\n"));
    194 }
    195 
    196 /*
    197  * rsh - remote shell
    198  */
    199 /* VARARGS */
    200 int
    201 main(int argc, char **argv)
    202 {
    203 	int c, rem;
    204 	char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x;
    205 	char *host = NULL, *user = NULL;
    206 	int cc;
    207 	boolean_t asrsh = B_FALSE;
    208 	struct passwd *pwd;
    209 	boolean_t readfrom_rem;
    210 	boolean_t readfrom_rfd2;
    211 	int one = 1;
    212 	int omask;
    213 	boolean_t nflag = B_FALSE;
    214 	char *krb_realm = NULL;
    215 	krb5_flags authopts;
    216 	krb5_error_code status;
    217 	enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
    218 	uid_t uid = getuid();
    219 
    220 	c = (argc + 1) * sizeof (char *);
    221 	if ((argv0 = malloc(c)) == NULL) {
    222 		perror("malloc");
    223 		return (EXIT_FAILURE);
    224 	}
    225 	(void) memcpy(argv0, argv, c);
    226 
    227 	(void) setlocale(LC_ALL, "");
    228 
    229 	(void) textdomain(TEXT_DOMAIN);
    230 
    231 	/*
    232 	 * Determine command name used to invoke to rlogin(1). Users can
    233 	 * create links named by a host pointing to the binary and type
    234 	 * "hostname" to log into that host afterwards.
    235 	 */
    236 	cmd = strrchr(argv[0], '/');
    237 	cmd = (cmd != NULL) ? (cmd + 1) : argv[0];
    238 
    239 	/*
    240 	 *	Add "remsh" as an alias for "rsh" (System III, V networking
    241 	 *	add-ons often used this name for the remote shell since rsh
    242 	 *	was already taken for the restricted shell).  Note that this
    243 	 *	usurps the ability to use "remsh" as the name of a host (by
    244 	 *	symlinking it to rsh), so we go one step farther:  if the
    245 	 *	file "/usr/bin/remsh" does not exist, we behave as if "remsh"
    246 	 *	is a host name.  If it does exist, we accept "remsh" as an
    247 	 *	"rsh" alias.
    248 	 */
    249 	if (strcmp(cmd, "remsh") == 0) {
    250 		struct stat sb;
    251 
    252 		if (stat("/usr/bin/remsh", &sb) < 0)
    253 			host = cmd;
    254 	} else if (strcmp(cmd, "rsh") != 0) {
    255 		host = cmd;
    256 	}
    257 
    258 	/* Handle legacy synopsis "rsh hostname options [command]". */
    259 	if (host == NULL) {
    260 		if (argc < 2)
    261 			usage();
    262 		if (*argv[1] != '-') {
    263 			host = argv[1];
    264 			argc--;
    265 			argv[1] = argv[0];
    266 			argv++;
    267 			asrsh = B_TRUE;
    268 		}
    269 	}
    270 
    271 	while ((c = getopt(argc, argv,
    272 	    DEBUGOPTSTRING "8AFLP:ade:fk:l:nwx")) != -1) {
    273 		switch (c) {
    274 #ifdef DEBUG
    275 		case 'D':
    276 			portnumber = htons(atoi(optarg));
    277 			krb5auth_flag = B_TRUE;
    278 			break;
    279 #endif /* DEBUG */
    280 		case 'F':
    281 			if (fflag)
    282 				usage_forward();
    283 			Fflag = 1;
    284 			krb5auth_flag = B_TRUE;
    285 			fwdable_done = B_TRUE;
    286 			break;
    287 		case 'f':
    288 			if (Fflag)
    289 				usage_forward();
    290 			fflag = 1;
    291 			krb5auth_flag = B_TRUE;
    292 			fwd_done = B_TRUE;
    293 			break;
    294 		case 'P':
    295 			if (strcmp(optarg, "N") == 0)
    296 				kcmd_proto = KCMD_NEW_PROTOCOL;
    297 			else if (strcmp(optarg, "O") == 0)
    298 				kcmd_proto = KCMD_OLD_PROTOCOL;
    299 			else
    300 				die(gettext("rsh: Only -PN or -PO "
    301 				    "allowed.\n"));
    302 			if (rcmdoption_done)
    303 				die(gettext("rsh: Only one of -PN and -PO "
    304 				    "allowed.\n"));
    305 			rcmdoption_done = B_TRUE;
    306 			krb5auth_flag = B_TRUE;
    307 			break;
    308 		case 'a':
    309 			krb5auth_flag = B_TRUE;
    310 			break;
    311 		case 'd':
    312 			options |= SO_DEBUG;
    313 			break;
    314 		case 'k':
    315 			krb_realm = optarg;
    316 			krb5auth_flag = B_TRUE;
    317 			break;
    318 		case 'l':
    319 			user = optarg;
    320 			break;
    321 		case 'n':
    322 			if (!nflag) {
    323 				if (close(STDIN_FILENO) < 0) {
    324 					perror("close");
    325 					return (EXIT_FAILURE);
    326 				}
    327 				/*
    328 				 * "STDION_FILENO" defined to 0 by POSIX
    329 				 * and hence the lowest file descriptor.
    330 				 * So the open(2) below is guaranteed to
    331 				 * reopen it because we closed it above.
    332 				 */
    333 				if (open("/dev/null", O_RDONLY) < 0) {
    334 					perror("open");
    335 					return (EXIT_FAILURE);
    336 				}
    337 				nflag = B_TRUE;
    338 			}
    339 			break;
    340 		case 'x':
    341 			encrypt_flag = 1;
    342 			krb5auth_flag = B_TRUE;
    343 			encrypt_done = B_TRUE;
    344 			break;
    345 		/*
    346 		 * Ignore the -L, -w, -e and -8 flags to allow aliases with
    347 		 * rlogin to work. Actually rlogin(1) doesn't understand
    348 		 * -w either but because "rsh -w hostname command" used
    349 		 * to work we still accept it.
    350 		 */
    351 		case '8':
    352 		case 'L':
    353 		case 'e':
    354 		case 'w':
    355 		/*
    356 		 * On the lines of the -L, -w, -e and -8 options above, we
    357 		 * ignore the -A option too, in order to allow aliases with
    358 		 * rlogin to work.
    359 		 *
    360 		 * Mind you !, the -a option to trigger Kerberos authentication
    361 		 * in rsh, has a totally different usage in rlogin, its the
    362 		 * -A option (in rlogin) which needs to be used to talk
    363 		 * Kerberos.
    364 		 */
    365 		case 'A':
    366 			break;
    367 		default:
    368 			usage();
    369 		}
    370 	}
    371 
    372 	argc -= optind;
    373 	argv += optind;
    374 
    375 	if (host == NULL) {
    376 		if (argc == 0)
    377 			usage();
    378 		argc--;
    379 		host = *argv++;
    380 		asrsh = B_TRUE;
    381 	}
    382 
    383 	if (argc == 0) {
    384 		(void) setreuid(uid, uid);
    385 		if (nflag)
    386 			usage();
    387 		if (asrsh)
    388 			*argv0 = "rlogin";
    389 		(void) execv(rlogin_path, argv0);
    390 		perror(rlogin_path);
    391 
    392 		(void) fprintf(stderr, gettext("No local rlogin "
    393 				"program found.\n"));
    394 		return (EXIT_FAILURE);
    395 	}
    396 
    397 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
    398 		(void) fprintf(stderr,
    399 		    gettext("Insufficient privileges, "
    400 			"rsh must be set-uid root\n"));
    401 		return (EXIT_FAILURE);
    402 	}
    403 
    404 	pwd = getpwuid(uid);
    405 	if (pwd == NULL) {
    406 		(void) fprintf(stderr, gettext("who are you?\n"));
    407 		return (EXIT_FAILURE);
    408 	}
    409 	if (user == NULL)
    410 		user = pwd->pw_name;
    411 
    412 	if (krb5auth_flag) {
    413 		status = krb5_init_context(&bsd_context);
    414 		if (status) {
    415 			com_err("rsh", status, "while initializing krb5");
    416 			return (EXIT_FAILURE);
    417 		}
    418 
    419 		/*
    420 		 * Get our local realm to look up local realm options.
    421 		 */
    422 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
    423 		if (status) {
    424 			com_err("rsh", status,
    425 				gettext("while getting default realm"));
    426 			return (EXIT_FAILURE);
    427 		}
    428 		/*
    429 		 * Check the realms section in krb5.conf for encryption,
    430 		 * forward & forwardable info
    431 		 */
    432 		profile_get_options_boolean(bsd_context->profile, realmdef,
    433 						option);
    434 		/*
    435 		 * Check the appdefaults section
    436 		 */
    437 		profile_get_options_boolean(bsd_context->profile, appdef,
    438 						option);
    439 		profile_get_options_string(bsd_context->profile, appdef,
    440 						rcmdversion);
    441 		/*
    442 		 * Set the *_flag variables, if the corresponding *_done are
    443 		 * set to 1, because we dont want the config file values
    444 		 * overriding the command line options.
    445 		 */
    446 		if (encrypt_done)
    447 			encrypt_flag = 1;
    448 		if (fwd_done) {
    449 			fflag = 1;
    450 			Fflag = 0;
    451 		} else if (fwdable_done) {
    452 			Fflag = 1;
    453 			fflag = 0;
    454 		}
    455 		if (!rcmdoption_done && (rcmdproto != NULL)) {
    456 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
    457 				kcmd_proto = KCMD_NEW_PROTOCOL;
    458 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
    459 				kcmd_proto = KCMD_OLD_PROTOCOL;
    460 			} else {
    461 				(void) fprintf(stderr, gettext("Unrecognized "
    462 					"KCMD protocol (%s)"), rcmdproto);
    463 				return (EXIT_FAILURE);
    464 			}
    465 		}
    466 
    467 
    468 		if (encrypt_flag && (!krb5_privacy_allowed())) {
    469 			(void) fprintf(stderr, gettext("rsh: Encryption not "
    470 				"supported.\n"));
    471 			return (EXIT_FAILURE);
    472 		}
    473 	}
    474 
    475 	/*
    476 	 * Connect with the service (shell/kshell) on the daemon side
    477 	 */
    478 	if (portnumber == 0) {
    479 		while (!init_service(krb5auth_flag)) {
    480 			/*
    481 			 * Connecting to the 'kshell' service failed,
    482 			 * fallback to normal rsh; Reset all KRB5 flags
    483 			 * and connect to 'shell' service on the server
    484 			 */
    485 			krb5auth_flag = B_FALSE;
    486 			encrypt_flag = fflag = Fflag = 0;
    487 		}
    488 	}
    489 
    490 	cc = encrypt_flag ? strlen(dash_x) : 0;
    491 	for (ap = argv; *ap != NULL; ap++)
    492 		cc += strlen(*ap) + 1;
    493 	cp = args = malloc(cc);
    494 	if (cp == NULL)
    495 		perror("malloc");
    496 	if (encrypt_flag) {
    497 		int length;
    498 
    499 		length = strlcpy(args, dash_x, cc);
    500 		cp += length;
    501 		cc -= length;
    502 	}
    503 	args_no_x = args;
    504 
    505 	for (ap = argv; *ap != NULL; ap++) {
    506 		int length;
    507 
    508 		length = strlcpy(cp, *ap, cc);
    509 		assert(length < cc);
    510 		cp += length;
    511 		cc -= length;
    512 		if (ap[1] != NULL) {
    513 			*cp++ = ' ';
    514 			cc--;
    515 		}
    516 	}
    517 
    518 	if (krb5auth_flag) {
    519 		authopts = AP_OPTS_MUTUAL_REQUIRED;
    520 		/*
    521 		 * Piggy-back forwarding flags on top of authopts;
    522 		 * they will be reset in kcmd
    523 		 */
    524 		if (fflag || Fflag)
    525 			authopts |= OPTS_FORWARD_CREDS;
    526 		if (Fflag)
    527 			authopts |= OPTS_FORWARDABLE_CREDS;
    528 
    529 		status = kcmd(&rem, &host, portnumber,
    530 				pwd->pw_name, user,
    531 				args, &rfd2, "host", krb_realm,
    532 				bsd_context, &auth_context, &cred,
    533 				NULL,	/* No need for sequence number */
    534 				NULL,	/* No need for server seq # */
    535 				authopts,
    536 				1,	/* Always set anyport */
    537 				&kcmd_proto);
    538 		if (status != 0) {
    539 			/*
    540 			 * If new protocol requested, we dont fallback to
    541 			 * less secure ones.
    542 			 */
    543 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    544 				(void) fprintf(stderr, gettext("rsh: kcmdv2 "
    545 					"to host %s failed - %s\n"
    546 					"Fallback to normal rsh denied."),
    547 					host, error_message(status));
    548 				return (EXIT_FAILURE);
    549 			}
    550 			/* check NO_TKT_FILE or equivalent... */
    551 			if (status != -1) {
    552 				(void) fprintf(stderr,
    553 				gettext("rsh: kcmd to host %s failed - %s\n"
    554 				"trying normal rsh...\n\n"),
    555 				host, error_message(status));
    556 			} else {
    557 				(void) fprintf(stderr,
    558 					gettext("trying normal rsh...\n"));
    559 			}
    560 			/*
    561 			 * kcmd() failed, so we now fallback to normal rsh,
    562 			 * after resetting the KRB5 flags and the 'args' array
    563 			 */
    564 			krb5auth_flag = B_FALSE;
    565 			encrypt_flag = fflag = Fflag = 0;
    566 			args = args_no_x;
    567 			(void) init_service(B_FALSE);
    568 		} else {
    569 			/*
    570 			 * Set up buffers for desread and deswrite.
    571 			 */
    572 			desinbuf.data = des_inbuf;
    573 			desoutbuf.data = des_outbuf;
    574 			desinbuf.length = sizeof (des_inbuf);
    575 			desoutbuf.length = sizeof (des_outbuf);
    576 
    577 			session_key = &cred->keyblock;
    578 
    579 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
    580 				status = krb5_auth_con_getlocalsubkey(
    581 				    bsd_context,
    582 				    auth_context,
    583 				    &session_key);
    584 				if (status) {
    585 					com_err("rsh", status,
    586 					    "determining subkey for session");
    587 					return (EXIT_FAILURE);
    588 				}
    589 				if (session_key == NULL) {
    590 					com_err("rsh", 0, "no subkey "
    591 					    "negotiated for connection");
    592 					return (EXIT_FAILURE);
    593 				}
    594 			}
    595 
    596 			eblock.crypto_entry = session_key->enctype;
    597 			eblock.key = (krb5_keyblock *)session_key;
    598 
    599 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
    600 			    &desinbuf, &desoutbuf, CLIENT, &eblock);
    601 			if (encrypt_flag) {
    602 				char *s = gettext("This rsh session is using "
    603 				    "encryption for all data transmissions.");
    604 				(void) write(STDERR_FILENO, s, strlen(s));
    605 				(void) write(STDERR_FILENO, "\r\n", 2);
    606 			}
    607 		}
    608 	}
    609 
    610 	/*
    611 	 * Don't merge this with the "if" statement above because
    612 	 * "krb5auth_flag" might be set to false inside it.
    613 	 */
    614 	if (!krb5auth_flag) {
    615 		rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args,
    616 		    &rfd2, AF_INET6);
    617 		if (rem < 0)
    618 			return (EXIT_FAILURE);
    619 	}
    620 	__priv_relinquish();
    621 
    622 	if (rfd2 < 0) {
    623 		(void) fprintf(stderr, gettext("rsh: can't establish "
    624 				"stderr\n"));
    625 		return (EXIT_FAILURE);
    626 	}
    627 	if (options & SO_DEBUG) {
    628 		if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one,
    629 		    sizeof (one)) < 0)
    630 			perror("rsh: setsockopt (stdin)");
    631 		if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one,
    632 		    sizeof (one)) < 0)
    633 			perror("rsh: setsockopt (stderr)");
    634 	}
    635 	omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
    636 
    637 	if (sigdisp(SIGINT) != SIG_IGN)
    638 		(void) sigset(SIGINT, sendsig);
    639 	if (sigdisp(SIGQUIT) != SIG_IGN)
    640 		(void) sigset(SIGQUIT, sendsig);
    641 	if (sigdisp(SIGTERM) != SIG_IGN)
    642 		(void) sigset(SIGTERM, sendsig);
    643 
    644 	if (nflag) {
    645 		(void) shutdown(rem, SHUT_WR);
    646 	} else {
    647 		child_pid = fork();
    648 		if (child_pid < 0) {
    649 			perror("rsh: fork");
    650 			return (EXIT_FAILURE);
    651 		}
    652 
    653 		if (!encrypt_flag) {
    654 			(void) ioctl(rfd2, FIONBIO, &one);
    655 			(void) ioctl(rem, FIONBIO, &one);
    656 		}
    657 
    658 		if (child_pid == 0) {
    659 			/* Child */
    660 			fd_set remset;
    661 			char *bp;
    662 			int  wc;
    663 			(void) close(rfd2);
    664 		reread:
    665 			errno = 0;
    666 			cc = read(0, buf, sizeof (buf));
    667 			if (cc <= 0)
    668 				goto done;
    669 			bp = buf;
    670 		rewrite:
    671 			FD_ZERO(&remset);
    672 			FD_SET(rem, &remset);
    673 			if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) {
    674 				if (errno != EINTR) {
    675 					perror("rsh: select");
    676 					return (EXIT_FAILURE);
    677 				}
    678 				goto rewrite;
    679 			}
    680 			if (!FD_ISSET(rem, &remset))
    681 				goto rewrite;
    682 			writeiv = B_FALSE;
    683 			wc = desrshwrite(rem, bp, cc);
    684 			if (wc < 0) {
    685 				if (errno == EWOULDBLOCK)
    686 					goto rewrite;
    687 				goto done;
    688 			}
    689 			cc -= wc; bp += wc;
    690 			if (cc == 0)
    691 				goto reread;
    692 			goto rewrite;
    693 		done:
    694 			(void) shutdown(rem, SHUT_WR);
    695 			return (EXIT_SUCCESS);
    696 		}
    697 	}
    698 
    699 #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
    700 
    701 	sigsetmask(omask);
    702 	readfrom_rem = B_TRUE;
    703 	readfrom_rfd2 = B_TRUE;
    704 	(void) sigset(SIGPIPE, sigpipehandler);
    705 	do {
    706 		fd_set readyset;
    707 
    708 		FD_ZERO(&readyset);
    709 		if (readfrom_rem)
    710 			FD_SET(rem, &readyset);
    711 		if (readfrom_rfd2)
    712 			FD_SET(rfd2, &readyset);
    713 		if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL,
    714 		    NULL) < 0) {
    715 			if (errno != EINTR) {
    716 				perror("rsh: select");
    717 				return (EXIT_FAILURE);
    718 			}
    719 			continue;
    720 		}
    721 		if (FD_ISSET(rfd2, &readyset)) {
    722 			errno = 0;
    723 			readiv = B_TRUE;
    724 			cc = desrshread(rfd2, buf, sizeof (buf));
    725 			if (cc <= 0) {
    726 				if (errno != EWOULDBLOCK)
    727 					readfrom_rfd2 = B_FALSE;
    728 			} else {
    729 				(void) write(STDERR_FILENO, buf, cc);
    730 			}
    731 		}
    732 		if (FD_ISSET(rem, &readyset)) {
    733 			errno = 0;
    734 			readiv = B_FALSE;
    735 			cc = desrshread(rem, buf, sizeof (buf));
    736 			if (cc <= 0) {
    737 				if (errno != EWOULDBLOCK)
    738 					readfrom_rem = B_FALSE;
    739 			} else
    740 				(void) write(STDOUT_FILENO, buf, cc);
    741 		}
    742 	} while (readfrom_rem || readfrom_rfd2);
    743 
    744 	if (!nflag)
    745 		(void) kill(child_pid, SIGKILL);
    746 	return (EXIT_SUCCESS);
    747 }
    748 
    749 static void
    750 sendsig(int signum)
    751 {
    752 	char	buffer;
    753 
    754 	writeiv = B_TRUE;
    755 	buffer = (char)signum;
    756 	(void) desrshwrite(rfd2, &buffer, 1);
    757 }
    758 
    759 static boolean_t
    760 init_service(boolean_t krb5flag)
    761 {
    762 	struct servent *sp;
    763 
    764 	if (krb5flag) {
    765 		sp = getservbyname("kshell", "tcp");
    766 		if (sp == NULL) {
    767 			(void) fprintf(stderr,
    768 				gettext("rsh: kshell/tcp: unknown service.\n"
    769 				"trying normal shell/tcp service\n"));
    770 			return (B_FALSE);
    771 		}
    772 	} else {
    773 		sp = getservbyname("shell", "tcp");
    774 		if (sp == NULL) {
    775 			portnumber = htons(IPPORT_CMDSERVER);
    776 			return (B_TRUE);
    777 		}
    778 	}
    779 
    780 	portnumber = sp->s_port;
    781 	return (B_TRUE);
    782 }
    783 
    784 static int
    785 desrshread(int fd, char *buf, int len)
    786 {
    787 	return (desread(fd, buf, len, readiv ? 1 : 0));
    788 }
    789 
    790 static int
    791 desrshwrite(int fd, char *buf, int len)
    792 {
    793 	return (deswrite(fd, buf, len, writeiv ? 1 : 0));
    794 }
    795