xref: /titanic_41/usr/src/cmd/cmd-inet/usr.bin/rlogin.c (revision 49218d4f8e4d84d1c08aeb267bcf6e451f2056dc)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * rlogin - remote login
44  */
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/errno.h>
48 #include <sys/file.h>
49 #include <sys/socket.h>
50 #include <sys/wait.h>
51 #include <sys/stropts.h>
52 #include <sys/ttold.h>
53 #include <sys/sockio.h>
54 #include <sys/tty.h>
55 #include <sys/ptyvar.h>
56 #include <sys/resource.h>
57 #include <sys/select.h>
58 #include <sys/time.h>
59 
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62 #include <priv_utils.h>
63 
64 #include <stdio.h>
65 #include <errno.h>
66 #include <pwd.h>
67 #include <signal.h>
68 #include <setjmp.h>
69 #include <netdb.h>
70 #include <fcntl.h>
71 #include <locale.h>
72 #include <stdarg.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76 
77 #include <k5-int.h>
78 #include <profile/prof_int.h>
79 #include <com_err.h>
80 #include <kcmd.h>
81 #include <krb5.h>
82 
83 /* signal disposition - signal handler or SIG_IGN, SIG_ERR, etc. */
84 typedef void (*sigdisp_t)(int);
85 
86 extern errcode_t	profile_get_options_boolean(profile_t, char **,
87     profile_options_boolean *);
88 extern errcode_t	profile_get_options_string(profile_t, char **,
89     profile_option_strings *);
90 
91 #define	RLOGIN_BUFSIZ	(1024 * 50)
92 static char des_inbuf[2 * RLOGIN_BUFSIZ];
93 					/* needs to be > largest read size */
94 static char des_outbuf[2 * RLOGIN_BUFSIZ];
95 					/* needs to be > largest write size */
96 static krb5_data desinbuf, desoutbuf;
97 static krb5_encrypt_block eblock;	/* eblock for encrypt/decrypt */
98 static krb5_keyblock *session_key;
99 static krb5_creds *cred;
100 static krb5_context bsd_context;
101 static krb5_auth_context auth_context;
102 
103 static char *krb_realm;
104 
105 static	int krb5auth_flag;	/* Flag set, when KERBEROS is enabled */
106 static	int fflag, Fflag;	/* Flag set, when option -f / -F used */
107 static	int encrypt_flag;	/* Flag set, when the "-x" option is used */
108 
109 /* Flag set, if -PN / -PO is specified */
110 static boolean_t rcmdoption_done;
111 
112 /* Flags set, if corres. cmd line options are turned on */
113 static boolean_t encrypt_done, fwd_done, fwdable_done;
114 
115 static profile_options_boolean option[] = {
116 	{ "encrypt", &encrypt_flag, 0 },
117 	{ "forward", &fflag, 0 },
118 	{ "forwardable", &Fflag, 0 },
119 	{ NULL, NULL, 0 }
120 };
121 
122 static char *rcmdproto;
123 static profile_option_strings rcmdversion[] = {
124 	{ "rcmd_protocol", &rcmdproto, 0 },
125 	{ NULL, NULL, 0 }
126 };
127 
128 static char rlogin[] = "rlogin";
129 
130 static char *realmdef[] = { "realms", NULL, rlogin, NULL };
131 static char *appdef[] = { "appdefaults", rlogin, NULL };
132 
133 #ifndef TIOCPKT_WINDOW
134 #define	TIOCPKT_WINDOW 0x80
135 #endif /* TIOCPKT_WINDOW */
136 
137 #ifndef sigmask
138 #define	sigmask(m)	(1 << ((m)-1))
139 #endif
140 
141 #define	set2mask(setp)	((setp)->__sigbits[0])
142 #define	mask2set(mask, setp) \
143 	((mask) == -1 ? sigfillset(setp) : (((setp)->__sigbits[0]) = (mask)))
144 
145 #ifdef DEBUG
146 #define	DEBUGOPTSTRING	"D:"
147 #else
148 #define	DEBUGOPTSTRING	""
149 #endif	/* DEBUG */
150 
151 static	boolean_t ttcompat;
152 static	struct termios savetty;
153 
154 static	char *errmsg(int);
155 static	char *host;
156 static	int port_number;
157 static	int rem = -1;
158 static	char cmdchar = '~';
159 static	boolean_t nocmdchar;
160 static	boolean_t eight;
161 static	boolean_t litout;
162 static	boolean_t null_local_username;
163 /*
164  * Note that this list of speeds is shorter than the list of speeds
165  * supported by termios.  This is because we can't be sure other rlogind's
166  * in the world will correctly cope with values other than what 4.2/4.3BSD
167  * supported.
168  */
169 static	char *speeds[] =
170 	{ "0", "50", "75", "110", "134", "150", "200", "300",
171 	    "600", "1200", "1800", "2400", "4800", "9600", "19200",
172 	    "38400" };
173 static	char term[256] = "network";
174 static	void lostpeer(void);
175 static	boolean_t dosigwinch;
176 static	struct winsize winsize;
177 static	void sigwinch(int);
178 static	void oob(void);
179 static	void doit(int);
180 static	sigdisp_t sigdisp(int);
181 
182 #define	CRLF "\r\n"
183 
184 static	pid_t child;
185 static	void catchild(int);
186 /* LINTED */
187 static	void copytochild(int);
188 static	void writeroob(int);
189 static	void stop(char), echo(char);
190 
191 static	int defflags, tabflag;
192 static	int deflflags;
193 static	char deferase, defkill;
194 static	struct tchars deftc;
195 static	struct ltchars defltc;
196 static	struct tchars notc = { (char)-1, (char)-1, (char)-1,
197 				(char)-1, (char)-1, (char)-1 };
198 static	struct ltchars noltc =	{ (char)-1, (char)-1, (char)-1,
199 				(char)-1, (char)-1, (char)-1 };
200 
201 static	void done(int);
202 static	void mode(int);
203 static	int reader(int);
204 static	void writer(void);
205 static	void prf(const char *, ...);
206 static	void sendwindow(void);
207 static	int compat_ioctl(int, int, void *);
208 
209 static void
210 sigsetmask(int mask)
211 {
212 	sigset_t oset;
213 	sigset_t nset;
214 
215 	(void) sigprocmask(0, NULL, &nset);
216 	mask2set(mask, &nset);
217 	(void) sigprocmask(SIG_SETMASK, &nset, &oset);
218 }
219 
220 static int
221 sigblock(int mask)
222 {
223 	sigset_t oset;
224 	sigset_t nset;
225 
226 	(void) sigprocmask(0, NULL, &nset);
227 	mask2set(mask, &nset);
228 	(void) sigprocmask(SIG_BLOCK, &nset, &oset);
229 	return (set2mask(&oset));
230 }
231 
232 static void
233 pop(int status) {
234 	if (ttcompat) {
235 		/*
236 		 * Pop ttcompat module
237 		 */
238 		(void) ioctl(STDIN_FILENO, I_POP, 0);
239 	}
240 	(void) tcsetattr(STDIN_FILENO, TCSANOW, &savetty);
241 	exit(status);
242 }
243 
244 static void
245 usage(void) {
246 	(void) fprintf(stderr, "%s\n%s\n",
247 	    gettext("usage: rlogin [-option] [-option...] "
248 		"[-k realm] [-l username] host"),
249 	    gettext("       where option is e, 8, E, L, A, a, x, "
250 		"PN / PO, f or F"));
251 	pop(EXIT_FAILURE);
252 }
253 
254 /* PRINTFLIKE(0) */
255 static void
256 die(const char *format, ...)
257 {
258 	va_list	ap;
259 
260 	va_start(ap, format);
261 	(void) vfprintf(stderr, format, ap);
262 	va_end(ap);
263 	usage();
264 }
265 
266 static void
267 usage_forward(void)
268 {
269 	die(gettext("rlogin: Only one of -f and -F allowed.\n"));
270 }
271 
272 int
273 main(int argc, char **argv)
274 {
275 	int c;
276 	char *cp, *cmd, *name = NULL;
277 	struct passwd *pwd;
278 	uid_t uid;
279 	int options = 0, oldmask;
280 	int on = 1;
281 	speed_t speed = 0;
282 	int getattr_ret;
283 	char *tmp;
284 	int sock;
285 	krb5_flags authopts;
286 	krb5_error_code status;
287 	enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
288 
289 	(void) setlocale(LC_ALL, "");
290 
291 #if !defined(TEXT_DOMAIN)
292 #define	TEXT_DOMAIN "SYS_TEST"
293 #endif
294 	(void) textdomain(TEXT_DOMAIN);
295 
296 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
297 		(void) fprintf(stderr,
298 		    gettext("Insufficient privileges, "
299 			"rlogin must be set-uid root\n"));
300 		exit(1);
301 	}
302 
303 	{
304 		int it;
305 
306 		if ((getattr_ret = tcgetattr(STDIN_FILENO, &savetty)) < 0)
307 			perror("tcgetattr");
308 		it = ioctl(STDIN_FILENO, I_FIND, "ttcompat");
309 		if (it < 0) {
310 			perror("ioctl I_FIND ttcompat");
311 			return (EXIT_FAILURE);
312 		}
313 		if (it == 0) {
314 			if (ioctl(STDIN_FILENO, I_PUSH, "ttcompat") < 0) {
315 				perror("ioctl I_PUSH ttcompat");
316 				exit(EXIT_FAILURE);
317 			}
318 			ttcompat = B_TRUE;
319 		}
320 	}
321 
322 	/*
323 	 * Determine command name used to invoke to rlogin(1). Users can
324 	 * create links named by a host pointing to the binary and type
325 	 * "hostname" to log into that host afterwards.
326 	 */
327 	cmd = strrchr(argv[0], '/');
328 	cmd = (cmd != NULL) ? (cmd + 1) : argv[0];
329 
330 	if (strcmp(cmd, rlogin) == 0) {
331 		if (argc < 2)
332 			usage();
333 		if (*argv[1] != '-') {
334 			host = argv[1];
335 			argc--;
336 			argv[1] = argv[0];
337 			argv++;
338 		}
339 	} else {
340 		host = cmd;
341 	}
342 
343 	while ((c = getopt(argc, argv,
344 	    DEBUGOPTSTRING "8AEFLP:ade:fk:l:x")) != -1) {
345 		switch (c) {
346 		case '8':
347 			eight = B_TRUE;
348 			break;
349 		case 'A':
350 			krb5auth_flag = B_TRUE;
351 			break;
352 #ifdef DEBUG
353 		case 'D':
354 			portnumber = htons(atoi(optarg));
355 			krb5auth_flag = B_TRUE;
356 			break;
357 #endif /* DEBUG */
358 		case 'E':
359 			nocmdchar = B_TRUE;
360 			break;
361 		case 'F':
362 			if (fflag)
363 				usage_forward();
364 			Fflag = 1;
365 			krb5auth_flag = B_TRUE;
366 			fwdable_done = B_TRUE;
367 			break;
368 		case 'f':
369 			if (Fflag)
370 				usage_forward();
371 			fflag = 1;
372 			krb5auth_flag = B_TRUE;
373 			fwd_done = B_TRUE;
374 			break;
375 		case 'L':
376 			litout = B_TRUE;
377 			break;
378 		case 'P':
379 			if (strcmp(optarg, "N") == 0)
380 				kcmd_proto = KCMD_NEW_PROTOCOL;
381 			else if (strcmp(optarg, "O") == 0)
382 				kcmd_proto = KCMD_OLD_PROTOCOL;
383 			else
384 				die(gettext("rlogin: Only -PN or -PO "
385 				    "allowed.\n"));
386 			if (rcmdoption_done)
387 				die(gettext("rlogin: Only one of -PN and -PO "
388 				    "allowed.\n"));
389 			rcmdoption_done = B_TRUE;
390 			krb5auth_flag = B_TRUE;
391 			break;
392 		case 'a':
393 		/*
394 		 * Force the remote host to prompt for a password by sending
395 		 * a NULL username. This option is mutually exclusive with
396 		 * the -A, -x, -f, -F, -k <realm> options.
397 		 */
398 			null_local_username = B_TRUE;
399 			break;
400 		case 'd':
401 			options |= SO_DEBUG;
402 			break;
403 		case 'e': {
404 			int c;
405 
406 			cp = optarg;
407 
408 			if ((c = *cp) != '\\') {
409 				cmdchar = c;
410 			} else {
411 				c = cp[1];
412 				if (c == '\0' || c == '\\') {
413 					cmdchar = '\\';
414 				} else if (c >= '0' && c <= '7') {
415 					long lc;
416 
417 					lc = strtol(&cp[1], NULL, 8);
418 					if (lc < 0 || lc > 255)
419 						die(gettext("rlogin: octal "
420 						    "escape character %s too "
421 						    "large.\n"), cp);
422 					cmdchar = (char)lc;
423 				} else {
424 					die(gettext("rlogin: unrecognized "
425 					    "escape character option %s.\n"),
426 					    cp);
427 				}
428 			}
429 			break;
430 		}
431 		case 'k':
432 			krb_realm = optarg;
433 			krb5auth_flag = B_TRUE;
434 			break;
435 		case 'l':
436 			name = optarg;
437 			break;
438 		case 'x':
439 			encrypt_flag = 1;
440 			krb5auth_flag = B_TRUE;
441 			encrypt_done = B_TRUE;
442 			break;
443 		default:
444 			usage();
445 		}
446 	}
447 
448 	argc -= optind;
449 	argv += optind;
450 
451 	if (host == NULL) {
452 		if (argc == 0)
453 			usage();
454 		argc--;
455 		host = *argv++;
456 	}
457 
458 	if (argc > 0)
459 		usage();
460 
461 	pwd = getpwuid(uid = getuid());
462 	if (pwd == NULL) {
463 		(void) fprintf(stderr, gettext("getpwuid(): can not find "
464 			"password entry for user id %d."), uid);
465 		return (EXIT_FAILURE);
466 	}
467 	if (name == NULL)
468 		name = pwd->pw_name;
469 
470 	/*
471 	 * If the `-a' option is issued on the cmd line, we reset all
472 	 * flags associated with other KRB5 specific options, since
473 	 * the -a option is mutually exclusive with the rest.
474 	 */
475 	if (null_local_username) {
476 		krb5auth_flag = B_FALSE;
477 		fflag = Fflag = encrypt_flag = 0;
478 		(void) fprintf(stderr, gettext("Note: The -a option nullifies "
479 					"all other Kerberos-specific\noptions "
480 					"you may have used.\n"));
481 	}
482 
483 	if (krb5auth_flag) {
484 		status = krb5_init_context(&bsd_context);
485 		if (status) {
486 			com_err(rlogin, status, gettext("while initializing"
487 					" krb5"));
488 			return (EXIT_FAILURE);
489 		}
490 		/*
491 		 * Set up buffers for desread and deswrite.
492 		 */
493 		desinbuf.data = des_inbuf;
494 		desoutbuf.data = des_outbuf;
495 		desinbuf.length = sizeof (des_inbuf);
496 		desoutbuf.length = sizeof (des_outbuf);
497 
498 		/*
499 		 * Get our local realm to look up local realm options.
500 		 */
501 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
502 		if (status) {
503 			com_err(rlogin, status,
504 				gettext("while getting default realm"));
505 			return (EXIT_FAILURE);
506 		}
507 		/*
508 		 * Check the realms section in krb5.conf for encryption,
509 		 * forward & forwardable info
510 		 */
511 		profile_get_options_boolean(bsd_context->profile, realmdef,
512 						option);
513 		/*
514 		 * Check the appdefaults section
515 		 */
516 		profile_get_options_boolean(bsd_context->profile, appdef,
517 						option);
518 		profile_get_options_string(bsd_context->profile, appdef,
519 						rcmdversion);
520 
521 		/*
522 		 * Set the *_flag variables, if the corresponding *_done are
523 		 * set to 1, because we dont want the config file values
524 		 * overriding the command line options.
525 		 */
526 		if (encrypt_done)
527 			encrypt_flag = 1;
528 		if (fwd_done) {
529 			fflag = 1;
530 			Fflag = 0;
531 		} else if (fwdable_done) {
532 			Fflag = 1;
533 			fflag = 0;
534 		}
535 		if (!rcmdoption_done && (rcmdproto != NULL)) {
536 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
537 				kcmd_proto = KCMD_NEW_PROTOCOL;
538 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
539 				kcmd_proto = KCMD_OLD_PROTOCOL;
540 			} else {
541 				(void) fprintf(stderr, gettext("Unrecognized "
542 					"KCMD protocol (%s)"), rcmdproto);
543 				return (EXIT_FAILURE);
544 			}
545 		}
546 
547 		if (encrypt_flag && (!krb5_privacy_allowed())) {
548 			(void) fprintf(stderr, gettext("rlogin: "
549 					"Encryption not supported.\n"));
550 			return (EXIT_FAILURE);
551 		}
552 	}
553 
554 	if (port_number == 0) {
555 		if (krb5auth_flag) {
556 			struct servent *sp;
557 
558 			/*
559 			 * If the krb5auth_flag is set (via -A, -f, -F, -k) &
560 			 * if there is an entry in /etc/services for Kerberos
561 			 * login, attempt to login with Kerberos. If we fail
562 			 * at any step,  use the standard rlogin
563 			 */
564 			sp = getservbyname(encrypt_flag ?
565 			    "eklogin" : "klogin", "tcp");
566 			if (sp == NULL) {
567 				port_number = encrypt_flag ?
568 				    htons(2105) : htons(543);
569 			} else {
570 				port_number = sp->s_port;
571 			}
572 		} else {
573 			port_number = htons(IPPORT_LOGINSERVER);
574 		}
575 	}
576 
577 	cp = getenv("TERM");
578 	if (cp) {
579 		(void) strncpy(term, cp, sizeof (term));
580 		term[sizeof (term) - 1] = '\0';
581 	}
582 	if (getattr_ret == 0) {
583 		speed = cfgetospeed(&savetty);
584 		/*
585 		 * "Be conservative in what we send" -- Only send baud rates
586 		 * which at least all 4.x BSD derivatives are known to handle
587 		 * correctly.
588 		 * NOTE:  This code assumes new termios speed values will
589 		 * be "higher" speeds.
590 		 */
591 		if (speed > B38400)
592 			speed = B38400;
593 	}
594 
595 	/*
596 	 * Only put the terminal speed info in if we have room
597 	 * so we don't overflow the buffer, and only if we have
598 	 * a speed we recognize.
599 	 */
600 	if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) &&
601 	    strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 <
602 	    sizeof (term)) {
603 		(void) strcat(term, "/");
604 		(void) strcat(term, speeds[speed]);
605 	}
606 	(void) sigset(SIGPIPE, (sigdisp_t)lostpeer);
607 	/* will use SIGUSR1 for window size hack, so hold it off */
608 	oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
609 
610 	/*
611 	 * Determine if v4 literal address and if so store it to one
612 	 * side. This is to correct the undesired behaviour of rcmd_af
613 	 * which converts a passed in v4 literal address to a v4 mapped
614 	 * v6 literal address. If it was a v4 literal we then re-assign
615 	 * it to host.
616 	 */
617 	tmp = NULL;
618 	if (inet_addr(host) != (in_addr_t)-1)
619 		tmp = host;
620 
621 	if (krb5auth_flag) {
622 		authopts = AP_OPTS_MUTUAL_REQUIRED;
623 
624 		/* Piggy-back forwarding flags on top of authopts; */
625 		/* they will be reset in kcmd */
626 		if (fflag || Fflag)
627 			authopts |= OPTS_FORWARD_CREDS;
628 		if (Fflag)
629 			authopts |= OPTS_FORWARDABLE_CREDS;
630 
631 		status = kcmd(&sock, &host, port_number,
632 			null_local_username ? "" : pwd->pw_name,
633 			name, term, NULL,
634 			"host", krb_realm, bsd_context, &auth_context,
635 			&cred,
636 			NULL,		/* No need for sequence number */
637 			NULL,		/* No need for server seq # */
638 			authopts,
639 			0,		/* Not any port # */
640 			&kcmd_proto);
641 
642 		if (status != 0) {
643 			/*
644 			 * If new protocol requested, we dont fallback to
645 			 * less secure ones.
646 			 */
647 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
648 				(void) fprintf(stderr, gettext("rlogin: kcmdv2 "
649 					"to host %s failed - %s\n"
650 					"Fallback to normal rlogin denied."),
651 					host, error_message(status));
652 				return (EXIT_FAILURE);
653 			}
654 			if (status != -1) {
655 				(void) fprintf(stderr, gettext("rlogin: kcmd "
656 						"to host %s failed - %s,\n"
657 						"trying normal rlogin...\n\n"),
658 						host, error_message(status));
659 			} else {
660 				(void) fprintf(stderr,
661 					gettext("trying normal rlogin...\n"));
662 			}
663 			/*
664 			 * kcmd() failed, so we have to
665 			 * fallback to normal rlogin
666 			 */
667 			port_number = htons(IPPORT_LOGINSERVER);
668 			krb5auth_flag = B_FALSE;
669 			fflag = Fflag = encrypt_flag = 0;
670 			null_local_username = B_FALSE;
671 		} else {
672 			(void) fprintf(stderr,
673 			    gettext("connected with Kerberos V5\n"));
674 
675 			/*
676 			 * Setup eblock for desread and deswrite.
677 			 */
678 			session_key = &cred->keyblock;
679 
680 			if (kcmd_proto == KCMD_NEW_PROTOCOL) {
681 				status = krb5_auth_con_getlocalsubkey(
682 				    bsd_context,
683 				    auth_context,
684 				    &session_key);
685 				if (status) {
686 					com_err(rlogin, status,
687 					    "determining subkey for session");
688 					return (EXIT_FAILURE);
689 				}
690 				if (session_key == NULL) {
691 					com_err(rlogin, 0,
692 					    "no subkey negotiated for "
693 					    "connection");
694 					return (EXIT_FAILURE);
695 				}
696 			}
697 
698 			eblock.crypto_entry = session_key->enctype;
699 			eblock.key = (krb5_keyblock *)session_key;
700 
701 			init_encrypt(encrypt_flag, bsd_context, kcmd_proto,
702 			    &desinbuf, &desoutbuf, CLIENT, &eblock);
703 
704 			rem = sock;
705 			if (rem < 0)
706 				pop(EXIT_FAILURE);
707 		}
708 	}
709 
710 	/*
711 	 * Don't merge this with the "if" statement above because
712 	 * "krb5auth_flag" might be set to false inside it.
713 	 */
714 	if (!krb5auth_flag) {
715 		rem = rcmd_af(&host, port_number,
716 			null_local_username ? "" : pwd->pw_name,
717 			name, term, NULL, AF_INET6);
718 		if (rem < 0)
719 			pop(EXIT_FAILURE);
720 	}
721 
722 	/* Never need our privilege again */
723 	__priv_relinquish();
724 
725 	if (tmp != NULL)
726 		host = tmp;
727 
728 	if (options & SO_DEBUG &&
729 	    setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on,
730 			    sizeof (on)) < 0)
731 		perror("rlogin: setsockopt (SO_DEBUG)");
732 
733 	{
734 		int bufsize = 8192;
735 
736 		(void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize,
737 			sizeof (int));
738 	}
739 
740 	doit(oldmask);
741 	return (0);
742 }
743 
744 static void
745 doit(int oldmask)
746 {
747 	struct sgttyb sb;
748 	int atmark;
749 
750 	if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
751 		perror("ioctl TIOCGETP");
752 	defflags = sb.sg_flags;
753 	tabflag = defflags & O_TBDELAY;
754 	defflags &= ECHO | O_CRMOD;
755 	deferase = sb.sg_erase;
756 	defkill = sb.sg_kill;
757 	if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&deflflags) == -1)
758 		perror("ioctl TIOCLGET");
759 	if (ioctl(STDIN_FILENO, TIOCGETC, (char *)&deftc) == -1)
760 		perror("ioctl TIOCGETC");
761 	notc.t_startc = deftc.t_startc;
762 	notc.t_stopc = deftc.t_stopc;
763 	if (ioctl(STDIN_FILENO, TIOCGLTC, (char *)&defltc) == -1)
764 		perror("ioctl TIOCGLTC");
765 	(void) sigset(SIGINT, SIG_IGN);
766 	if (sigdisp(SIGHUP) != SIG_IGN)
767 		(void) sigset(SIGHUP, exit);
768 	if (sigdisp(SIGQUIT) != SIG_IGN)
769 		(void) sigset(SIGQUIT, exit);
770 	child = fork();
771 	if (child == (pid_t)-1) {
772 		perror("rlogin: fork");
773 		done(EXIT_FAILURE);
774 	}
775 	if (child == 0) {
776 		mode(1);
777 		if (reader(oldmask) == 0) {
778 			prf(gettext("Connection to %.*s closed."),
779 			    MAXHOSTNAMELEN, host);
780 			exit(EXIT_SUCCESS);
781 		}
782 		(void) sleep(1);
783 		prf(gettext("\aConnection to %.*s closed."),
784 		    MAXHOSTNAMELEN, host);
785 		exit(EXIT_FAILURE);
786 	}
787 
788 	/*
789 	 * We may still own the socket, and may have a pending SIGURG (or might
790 	 * receive one soon) that we really want to send to the reader.  Set a
791 	 * trap that simply copies such signals to the child.
792 	 */
793 #ifdef F_SETOWN_BUG_FIXED
794 	(void) sigset(SIGURG, copytochild);
795 #else
796 	(void) sigset(SIGURG, SIG_IGN);
797 #endif /* F_SETOWN_BUG_FIXED */
798 	(void) sigset(SIGUSR1, writeroob);
799 	/*
800 	 * Of course, if the urgent byte already arrived, allowing SIGURG
801 	 * won't get us notification.  So, we check to see if we've got
802 	 * an urgent byte.  If so, force a call to writeroob() to pretend
803 	 * we got SIGURG.
804 	 */
805 	if (ioctl(rem, SIOCATMARK, &atmark) >= 0) {
806 		if (atmark)
807 			writeroob(0);
808 	}
809 	sigsetmask(oldmask);
810 	(void) sigset(SIGCHLD, catchild);
811 	writer();
812 	prf(gettext("Closed connection to %.*s."), MAXHOSTNAMELEN, host);
813 	done(EXIT_SUCCESS);
814 }
815 
816 /*
817  * Get signal disposition (or signal handler) for a given signal
818  */
819 static sigdisp_t
820 sigdisp(int sig)
821 {
822 	struct sigaction act;
823 
824 	act.sa_handler = NULL;
825 	act.sa_flags = 0;
826 	(void) sigemptyset(&act.sa_mask);
827 	(void) sigaction(sig, NULL, &act);
828 	return (act.sa_handler);
829 }
830 
831 static void
832 done(int status)
833 {
834 	pid_t w;
835 
836 	mode(0);
837 	if (child > 0) {
838 		/* make sure catchild does not snap it up */
839 		(void) sigset(SIGCHLD, SIG_DFL);
840 		if (kill(child, SIGKILL) >= 0)
841 			while ((w = wait(0)) > (pid_t)0 && w != child)
842 				/* void */;
843 	}
844 	pop(status);
845 }
846 
847 /*
848  * Copy SIGURGs to the child process.
849  */
850 
851 /* ARGSUSED */
852 static void
853 copytochild(int signum)
854 {
855 
856 	(void) kill(child, SIGURG);
857 }
858 
859 /*
860  * This is called when the reader process gets the out-of-band (urgent)
861  * request to turn on the window-changing protocol.
862  */
863 
864 /* ARGSUSED */
865 static void
866 writeroob(int signum)
867 {
868 	int mask;
869 
870 	if (!dosigwinch) {
871 		/*
872 		 * Start tracking window size.  It doesn't matter which
873 		 * order the next two are in, because we'll be unconditionally
874 		 * sending a size notification in a moment.
875 		 */
876 		(void) sigset(SIGWINCH, sigwinch);
877 		dosigwinch = B_TRUE;
878 
879 		/*
880 		 * It would be bad if a SIGWINCH came in between the ioctl
881 		 * and sending the data.  It could result in the SIGWINCH
882 		 * handler sending a good message, and then us sending an
883 		 * outdated or inconsistent message.
884 		 *
885 		 * Instead, if the change is made before the
886 		 * ioctl, the sigwinch handler will send a size message
887 		 * and we'll send another, identical, one.  If the change
888 		 * is made after the ioctl, we'll send a message with the
889 		 * old value, and then the sigwinch handler will send
890 		 * a revised, correct one.
891 		 */
892 		mask = sigblock(sigmask(SIGWINCH));
893 		if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) == 0)
894 			sendwindow();
895 		sigsetmask(mask);
896 	}
897 }
898 
899 /* ARGSUSED */
900 static void
901 catchild(int signum)
902 {
903 	int options;
904 	siginfo_t	info;
905 	int error;
906 
907 	for (;;) {
908 		options = WNOHANG | WEXITED;
909 		error = waitid(P_ALL, 0, &info, options);
910 		if (error != 0)
911 			return;
912 		if (info.si_pid == 0)
913 			return;
914 		if (info.si_code == CLD_TRAPPED)
915 			continue;
916 		if (info.si_code == CLD_STOPPED)
917 			continue;
918 		done(info.si_status);
919 	}
920 }
921 
922 /*
923  * writer: write to remote: 0 -> line.
924  * ~.	terminate
925  * ~^Z	suspend rlogin process.
926  * ~^Y  suspend rlogin process, but leave reader alone.
927  */
928 static void
929 writer(void)
930 {
931 	char c;
932 	int n;
933 	boolean_t bol = B_TRUE;		/* beginning of line */
934 	boolean_t local = B_FALSE;
935 
936 	for (;;) {
937 		n = read(STDIN_FILENO, &c, 1);
938 		if (n <= 0) {
939 			if (n == 0)
940 				break;
941 			if (errno == EINTR)
942 				continue;
943 			else {
944 				prf(gettext("Read error from terminal: %s"),
945 				    errmsg(errno));
946 				break;
947 			}
948 		}
949 		/*
950 		 * If we're at the beginning of the line
951 		 * and recognize a command character, then
952 		 * we echo locally.  Otherwise, characters
953 		 * are echo'd remotely.  If the command
954 		 * character is doubled, this acts as a
955 		 * force and local echo is suppressed.
956 		 */
957 		if (bol && !nocmdchar) {
958 			bol = B_FALSE;
959 			if (c == cmdchar) {
960 				local = B_TRUE;
961 				continue;
962 			}
963 		} else if (local) {
964 			local = B_FALSE;
965 			if (c == '.' || c == deftc.t_eofc) {
966 				echo(c);
967 				break;
968 			}
969 			if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
970 				bol = B_TRUE;
971 				echo(c);
972 				stop(c);
973 				continue;
974 			}
975 			if (c != cmdchar) {
976 				if (deswrite(rem, &cmdchar, 1, 0) < 0) {
977 					prf(gettext(
978 					    "Write error to network: %s"),
979 					    errmsg(errno));
980 					break;
981 				}
982 			}
983 		}
984 		if ((n = deswrite(rem, &c, 1, 0)) <= 0) {
985 			if (n == 0)
986 				prf(gettext("line gone"));
987 			else
988 				prf(gettext("Write error to network: %s"),
989 				    errmsg(errno));
990 			break;
991 		}
992 		bol = c == defkill || c == deftc.t_eofc ||
993 		    c == deftc.t_intrc || c == defltc.t_suspc ||
994 		    c == '\r' || c == '\n';
995 	}
996 }
997 
998 static void
999 echo(char c)
1000 {
1001 	char buf[8];
1002 	char *p = buf;
1003 
1004 	c &= 0177;
1005 	*p++ = cmdchar;
1006 	if (c < ' ') {
1007 		*p++ = '^';
1008 		*p++ = c + '@';
1009 	} else if (c == 0177) {
1010 		*p++ = '^';
1011 		*p++ = '?';
1012 	} else
1013 		*p++ = c;
1014 	*p++ = '\r';
1015 	*p++ = '\n';
1016 	if (write(STDOUT_FILENO, buf, p - buf) < 0)
1017 		prf(gettext("Write error to terminal: %s"), errmsg(errno));
1018 }
1019 
1020 static void
1021 stop(char cmdc)
1022 {
1023 	mode(0);
1024 	(void) sigset(SIGCHLD, SIG_IGN);
1025 	(void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
1026 	(void) sigset(SIGCHLD, catchild);
1027 	mode(1);
1028 	sigwinch(0);			/* check for size changes */
1029 }
1030 
1031 /* ARGSUSED */
1032 static void
1033 sigwinch(int signum)
1034 {
1035 	struct winsize ws;
1036 
1037 	if (dosigwinch && ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0 &&
1038 	    memcmp(&winsize, &ws, sizeof (ws)) != 0) {
1039 		winsize = ws;
1040 		sendwindow();
1041 	}
1042 }
1043 
1044 /*
1045  * Send the window size to the server via the magic escape.
1046  * Note:  SIGWINCH should be blocked when this is called, lest
1047  * winsize change underneath us and chaos result.
1048  */
1049 static void
1050 sendwindow(void)
1051 {
1052 	char obuf[4 + sizeof (struct winsize)];
1053 	struct winsize *wp = (struct winsize *)(void *)(obuf+4);
1054 
1055 	obuf[0] = -1;
1056 	obuf[1] = -1;
1057 	obuf[2] = 's';
1058 	obuf[3] = 's';
1059 	wp->ws_row = htons(winsize.ws_row);
1060 	wp->ws_col = htons(winsize.ws_col);
1061 	wp->ws_xpixel = htons(winsize.ws_xpixel);
1062 	wp->ws_ypixel = htons(winsize.ws_ypixel);
1063 	if (deswrite(rem, obuf, sizeof (obuf), 0) < 0)
1064 		prf(gettext("Write error to network: %s"), errmsg(errno));
1065 }
1066 
1067 
1068 /*
1069  * reader: read from remote: remote -> stdout
1070  */
1071 #define	READING	1
1072 #define	WRITING	2
1073 
1074 static	char rcvbuf[8 * 1024];
1075 static	int rcvcnt;
1076 static	int rcvstate;
1077 static	pid_t ppid;
1078 static	jmp_buf rcvtop;
1079 
1080 static void
1081 oob(void)
1082 {
1083 	int out = FWRITE, atmark, n;
1084 	int rcvd = 0;
1085 	char waste[4*BUFSIZ], mark;
1086 	struct sgttyb sb;
1087 	fd_set exceptfds;
1088 	struct timeval tv;
1089 	int ret;
1090 
1091 	FD_ZERO(&exceptfds);
1092 	FD_SET(rem, &exceptfds);
1093 	timerclear(&tv);
1094 	ret = select(rem+1, NULL, NULL, &exceptfds, &tv);
1095 	/*
1096 	 * We may get an extra signal at start up time since we are trying
1097 	 * to take all precautions not to miss the urgent byte. This
1098 	 * means we may get here without any urgent data to process, in which
1099 	 * case we do nothing and just return.
1100 	 */
1101 	if (ret <= 0)
1102 		return;
1103 
1104 	do {
1105 		if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
1106 			break;
1107 		}
1108 		if (!atmark) {
1109 			/*
1110 			 * Urgent data not here yet.
1111 			 * It may not be possible to send it yet
1112 			 * if we are blocked for output
1113 			 * and our input buffer is full.
1114 			 */
1115 			if (rcvcnt < sizeof (rcvbuf)) {
1116 				n = desread(rem, rcvbuf + rcvcnt,
1117 					sizeof (rcvbuf) - rcvcnt, 0);
1118 				if (n <= 0)
1119 					return;
1120 				rcvd += n;
1121 				rcvcnt += n;
1122 			} else {
1123 				/*
1124 				 * We still haven't gotten to the urgent mark
1125 				 * and we're out of buffer space.  Since we
1126 				 * must clear our receive window to allow it
1127 				 * to arrive, we will have to throw away
1128 				 * these bytes.
1129 				 */
1130 				n = desread(rem, waste, sizeof (waste), 0);
1131 				if (n <= 0)
1132 					return;
1133 			}
1134 		}
1135 	} while (atmark == 0);
1136 	while (recv(rem, &mark, 1, MSG_OOB) < 0) {
1137 		switch (errno) {
1138 
1139 		case EWOULDBLOCK:
1140 			/*
1141 			 * We've reached the urgent mark, so the next
1142 			 * data to arrive will be the urgent, but it must
1143 			 * not have arrived yet.
1144 			 */
1145 			(void) sleep(1);
1146 			continue;
1147 
1148 		default:
1149 			return;
1150 		}
1151 	}
1152 	if (mark & TIOCPKT_WINDOW) {
1153 		/*
1154 		 * Let server know about window size changes
1155 		 */
1156 		(void) kill(ppid, SIGUSR1);
1157 	}
1158 	if (!eight && (mark & TIOCPKT_NOSTOP)) {
1159 		if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
1160 			perror("ioctl TIOCGETP");
1161 		sb.sg_flags &= ~O_CBREAK;
1162 		sb.sg_flags |= O_RAW;
1163 		if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
1164 			perror("ioctl TIOCSETP 1");
1165 		notc.t_stopc = -1;
1166 		notc.t_startc = -1;
1167 		if (compat_ioctl(STDIN_FILENO, TIOCSETC, &notc) == -1)
1168 			perror("ioctl TIOCSETC");
1169 	}
1170 	if (!eight && (mark & TIOCPKT_DOSTOP)) {
1171 		if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
1172 			perror("ioctl TIOCGETP");
1173 		sb.sg_flags &= ~O_RAW;
1174 		sb.sg_flags |= O_CBREAK;
1175 		if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
1176 			perror("ioctl TIOCSETP 2");
1177 		notc.t_stopc = deftc.t_stopc;
1178 		notc.t_startc = deftc.t_startc;
1179 		if (compat_ioctl(STDIN_FILENO, TIOCSETC, &notc) == -1)
1180 			perror("ioctl TIOCSETC");
1181 	}
1182 	if (mark & TIOCPKT_FLUSHWRITE) {
1183 		if (ioctl(STDOUT_FILENO, TIOCFLUSH, (char *)&out) == -1)
1184 			perror("ioctl TIOCFLUSH");
1185 		for (;;) {
1186 			if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
1187 				perror("ioctl SIOCATMARK");
1188 				break;
1189 			}
1190 			if (atmark)
1191 				break;
1192 			n = desread(rem, waste, sizeof (waste), 0);
1193 			if (n <= 0) {
1194 				if (n < 0)
1195 					prf(gettext(
1196 					    "Read error from network: %s"),
1197 					    errmsg(errno));
1198 				break;
1199 			}
1200 		}
1201 		/*
1202 		 * Don't want any pending data to be output,
1203 		 * so clear the recv buffer.
1204 		 * If we were hanging on a write when interrupted,
1205 		 * don't want it to restart.  If we were reading,
1206 		 * restart anyway.
1207 		 */
1208 		rcvcnt = 0;
1209 		longjmp(rcvtop, 1);
1210 	}
1211 	/*
1212 	 * If we filled the receive buffer while a read was pending,
1213 	 * longjmp to the top to restart appropriately.  Don't abort
1214 	 * a pending write, however, or we won't know how much was written.
1215 	 */
1216 	if (rcvd && rcvstate == READING)
1217 		longjmp(rcvtop, 1);
1218 }
1219 
1220 /*
1221  * reader: read from remote: line -> 1
1222  */
1223 static int
1224 reader(int oldmask)
1225 {
1226 	/*
1227 	 * 4.3bsd or later and SunOS 4.0 or later use the posiitive
1228 	 * pid; otherwise use the negative.
1229 	 */
1230 	pid_t pid = getpid();
1231 	int n, remaining;
1232 	char *bufp = rcvbuf;
1233 
1234 	(void) sigset(SIGTTOU, SIG_IGN);
1235 	(void) sigset(SIGURG, (void (*)())oob);
1236 	ppid = getppid();
1237 	if (fcntl(rem, F_SETOWN, pid) == -1)
1238 		perror("fcntl F_SETOWN");
1239 	/*
1240 	 * A SIGURG may have been posted before we were completely forked,
1241 	 * which means we may not have received it. To insure we do not miss
1242 	 * any urgent data, we force the signal. The signal hander will be
1243 	 * able to determine if in fact there is urgent data or not.
1244 	 */
1245 	(void) kill(pid, SIGURG);
1246 	(void) setjmp(rcvtop);
1247 	sigsetmask(oldmask);
1248 	for (;;) {
1249 		while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
1250 			rcvstate = WRITING;
1251 			n = write(STDOUT_FILENO, bufp, remaining);
1252 			if (n < 0) {
1253 				if (errno != EINTR) {
1254 					prf(gettext(
1255 					    "Write error to terminal: %s"),
1256 					    errmsg(errno));
1257 					return (-1);
1258 				}
1259 				continue;
1260 			}
1261 			bufp += n;
1262 		}
1263 		bufp = rcvbuf;
1264 		rcvcnt = 0;
1265 		rcvstate = READING;
1266 		rcvcnt = desread(rem, rcvbuf, sizeof (rcvbuf), 0);
1267 		if (rcvcnt == 0)
1268 			return (0);
1269 		if (rcvcnt < 0) {
1270 			if (errno == EINTR)
1271 				continue;
1272 			prf(gettext("Read error from network: %s"),
1273 			    errmsg(errno));
1274 			return (-1);
1275 		}
1276 	}
1277 }
1278 
1279 static void
1280 mode(int f)
1281 {
1282 	struct tchars *tc;
1283 	struct ltchars *ltc;
1284 	struct sgttyb sb;
1285 	int	lflags;
1286 
1287 	if (ioctl(STDIN_FILENO, TIOCGETP, (char *)&sb) == -1)
1288 		perror("ioctl TIOCGETP");
1289 	if (ioctl(STDIN_FILENO, TIOCLGET, (char *)&lflags) == -1)
1290 		perror("ioctl TIOCLGET");
1291 	switch (f) {
1292 
1293 	case 0:
1294 		sb.sg_flags &= ~(O_CBREAK|O_RAW|O_TBDELAY);
1295 		sb.sg_flags |= defflags|tabflag;
1296 		tc = &deftc;
1297 		ltc = &defltc;
1298 		sb.sg_kill = defkill;
1299 		sb.sg_erase = deferase;
1300 		lflags = deflflags;
1301 		break;
1302 
1303 	case 1:
1304 		sb.sg_flags |= (eight ? O_RAW : O_CBREAK);
1305 		sb.sg_flags &= ~defflags;
1306 		/* preserve tab delays, but turn off XTABS */
1307 		if ((sb.sg_flags & O_TBDELAY) == O_XTABS)
1308 			sb.sg_flags &= ~O_TBDELAY;
1309 		tc = &notc;
1310 		ltc = &noltc;
1311 		sb.sg_kill = sb.sg_erase = -1;
1312 		if (litout)
1313 			lflags |= LLITOUT;
1314 		break;
1315 
1316 	default:
1317 		/*NOTREACHED*/
1318 		return;
1319 	}
1320 	if (compat_ioctl(STDIN_FILENO, TIOCSLTC, ltc) == -1)
1321 		perror("ioctl TIOCSLTC");
1322 	if (compat_ioctl(STDIN_FILENO, TIOCSETC, tc) == -1)
1323 		perror("ioctl TIOCSETC");
1324 	if (compat_ioctl(STDIN_FILENO, TIOCSETP, &sb) == -1)
1325 		perror("ioctl TIOCSETP 3");
1326 	if (compat_ioctl(STDIN_FILENO, TIOCLSET, &lflags) == -1)
1327 		perror("ioctl TIOCLSET");
1328 }
1329 
1330 /* PRINTFLIKE(0) */
1331 static void
1332 prf(const char *format, ...)
1333 {
1334 	va_list	ap;
1335 
1336 	va_start(ap, format);
1337 	(void) vfprintf(stderr, format, ap);
1338 	va_end(ap);
1339 	(void) fputs(CRLF, stderr);
1340 }
1341 
1342 static void
1343 lostpeer(void)
1344 {
1345 	(void) sigset(SIGPIPE, SIG_IGN);
1346 	prf(gettext("\aConnection to %.*s closed."), MAXHOSTNAMELEN, host);
1347 	done(EXIT_FAILURE);
1348 }
1349 
1350 static char *
1351 errmsg(int errcode)
1352 {
1353 	extern int sys_nerr;
1354 
1355 	if (errcode < 0 || errcode > sys_nerr)
1356 		return (gettext("Unknown error"));
1357 	else
1358 		return (strerror(errcode));
1359 }
1360 
1361 static int
1362 compat_ioctl(int des, int request, void *arg)
1363 {
1364 	struct termios	tb;
1365 	boolean_t	flag = B_FALSE;
1366 
1367 	if (ioctl(des, request, arg) < 0)
1368 		return (-1);
1369 
1370 	if (tcgetattr(des, &tb) < 0)
1371 		return (-1);
1372 
1373 	if (cfgetispeed(&tb) != cfgetispeed(&savetty)) {
1374 		(void) cfsetispeed(&tb, cfgetispeed(&savetty));
1375 		flag = B_TRUE;
1376 	}
1377 	if (cfgetospeed(&tb) != cfgetospeed(&savetty)) {
1378 		(void) cfsetospeed(&tb, cfgetospeed(&savetty));
1379 		flag = B_TRUE;
1380 	}
1381 
1382 	return (flag ? tcsetattr(des, TCSANOW, &tb) : 0);
1383 }
1384