xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/rsh.c (revision 2a8d6eba033e4713ab12b61178f0513f1f075482)
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