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