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