xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c (revision 32885d593baf8bac788fa78885893a51b3ad0f28)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
506e1a714Sraf  * Common Development and Distribution License (the "License").
606e1a714Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
2006e1a714Sraf  */
2106e1a714Sraf 
2206e1a714Sraf /*
23*32885d59Sgtb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
297c478bd9Sstevel@tonic-gate  * All Rights Reserved.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
347c478bd9Sstevel@tonic-gate  * The Regents of the University of California.
357c478bd9Sstevel@tonic-gate  * All Rights Reserved.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
387c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
397c478bd9Sstevel@tonic-gate  * contributors.
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * Telnet server.
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/param.h>
497c478bd9Sstevel@tonic-gate #include <sys/socket.h>
507c478bd9Sstevel@tonic-gate #include <sys/wait.h>
517c478bd9Sstevel@tonic-gate #include <sys/file.h>
527c478bd9Sstevel@tonic-gate #include <sys/stat.h>
537c478bd9Sstevel@tonic-gate #include <sys/filio.h>
547c478bd9Sstevel@tonic-gate #include <sys/time.h>
557c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
567c478bd9Sstevel@tonic-gate #include <sys/stream.h>
577c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
587c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
597c478bd9Sstevel@tonic-gate #include <unistd.h>
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #include <netinet/in.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate #define	AUTHWHO_STR
647c478bd9Sstevel@tonic-gate #define	AUTHTYPE_NAMES
657c478bd9Sstevel@tonic-gate #define	AUTHHOW_NAMES
667c478bd9Sstevel@tonic-gate #define	AUTHRSP_NAMES
677c478bd9Sstevel@tonic-gate #define	ENCRYPT_NAMES
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <arpa/telnet.h>
707c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
717c478bd9Sstevel@tonic-gate #include <stdio.h>
727c478bd9Sstevel@tonic-gate #include <stdarg.h>
737c478bd9Sstevel@tonic-gate #include <signal.h>
747c478bd9Sstevel@tonic-gate #include <errno.h>
757c478bd9Sstevel@tonic-gate #include <netdb.h>
767c478bd9Sstevel@tonic-gate #include <syslog.h>
777c478bd9Sstevel@tonic-gate #include <ctype.h>
787c478bd9Sstevel@tonic-gate #include <fcntl.h>
797c478bd9Sstevel@tonic-gate #include <sac.h>	/* for SC_WILDC */
807c478bd9Sstevel@tonic-gate #include <utmpx.h>
817c478bd9Sstevel@tonic-gate #include <sys/ttold.h>
827c478bd9Sstevel@tonic-gate #include <malloc.h>
837c478bd9Sstevel@tonic-gate #include <string.h>
847c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
857c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
867c478bd9Sstevel@tonic-gate #include <sys/logindmux.h>
877c478bd9Sstevel@tonic-gate #include <sys/telioctl.h>
887c478bd9Sstevel@tonic-gate #include <deflt.h>
897c478bd9Sstevel@tonic-gate #include <stdlib.h>
907c478bd9Sstevel@tonic-gate #include <string.h>
917c478bd9Sstevel@tonic-gate #include <stropts.h>
927c478bd9Sstevel@tonic-gate #include <termios.h>
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate #include <com_err.h>
957c478bd9Sstevel@tonic-gate #include <krb5.h>
967c478bd9Sstevel@tonic-gate #include <krb5_repository.h>
977c478bd9Sstevel@tonic-gate #include <des/des.h>
987c478bd9Sstevel@tonic-gate #include <rpc/des_crypt.h>
997c478bd9Sstevel@tonic-gate #include <sys/cryptmod.h>
1007c478bd9Sstevel@tonic-gate #include <bsm/adt.h>
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate #define	TELNETD_OPTS "Ss:a:dEXUhR:M:"
1037c478bd9Sstevel@tonic-gate #ifdef DEBUG
1047c478bd9Sstevel@tonic-gate #define	DEBUG_OPTS "p:e"
1057c478bd9Sstevel@tonic-gate #else
1067c478bd9Sstevel@tonic-gate #define	DEBUG_OPTS ""
1077c478bd9Sstevel@tonic-gate #endif /* DEBUG */
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate #define	OPT_NO			0		/* won't do this option */
1107c478bd9Sstevel@tonic-gate #define	OPT_YES			1		/* will do this option */
1117c478bd9Sstevel@tonic-gate #define	OPT_YES_BUT_ALWAYS_LOOK	2
1127c478bd9Sstevel@tonic-gate #define	OPT_NO_BUT_ALWAYS_LOOK	3
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate #define	MAXOPTLEN 256
1157c478bd9Sstevel@tonic-gate #define	MAXUSERNAMELEN 256
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static char	remopts[MAXOPTLEN];
1187c478bd9Sstevel@tonic-gate static char	myopts[MAXOPTLEN];
1197c478bd9Sstevel@tonic-gate static uchar_t	doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
1207c478bd9Sstevel@tonic-gate static uchar_t	dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
1217c478bd9Sstevel@tonic-gate static uchar_t	will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
1227c478bd9Sstevel@tonic-gate static uchar_t	wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
1237c478bd9Sstevel@tonic-gate /*
1247c478bd9Sstevel@tonic-gate  * I/O data buffers, pointers, and counters.
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate static char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate static char	*netibuf, *netip;
1297c478bd9Sstevel@tonic-gate static int	netibufsize;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate #define	NIACCUM(c)	{   *netip++ = c; \
1327c478bd9Sstevel@tonic-gate 			    ncc++; \
1337c478bd9Sstevel@tonic-gate 			}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
1367c478bd9Sstevel@tonic-gate static char	*neturg = 0;		/* one past last bye of urgent data */
1377c478bd9Sstevel@tonic-gate /* the remote system seems to NOT be an old 4.2 */
1387c478bd9Sstevel@tonic-gate static int	not42 = 1;
1397c478bd9Sstevel@tonic-gate static char	defaultfile[] = "/etc/default/telnetd";
1407c478bd9Sstevel@tonic-gate static char	bannervar[] = "BANNER=";
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate static char BANNER1[] = "\r\n\r\n";
1437c478bd9Sstevel@tonic-gate static char BANNER2[] = "\r\n\r\0\r\n\r\0";
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * buffer for sub-options - enlarged to 4096 to handle credentials
1477c478bd9Sstevel@tonic-gate  * from AUTH options
1487c478bd9Sstevel@tonic-gate  */
1497c478bd9Sstevel@tonic-gate static char	subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
1507c478bd9Sstevel@tonic-gate #define	SB_CLEAR()	subpointer = subbuffer;
1517c478bd9Sstevel@tonic-gate #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
1527c478bd9Sstevel@tonic-gate #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof (subbuffer))) { \
1537c478bd9Sstevel@tonic-gate 				*subpointer++ = (c); \
1547c478bd9Sstevel@tonic-gate 			}
1557c478bd9Sstevel@tonic-gate #define	SB_GET()	((*subpointer++)&0xff)
1567c478bd9Sstevel@tonic-gate #define	SB_EOF()	(subpointer >= subend)
1577c478bd9Sstevel@tonic-gate #define	SB_LEN()	(subend - subpointer)
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate #define	MAXERRSTRLEN 1024
1607c478bd9Sstevel@tonic-gate #define	MAXPRINCLEN 256
1617c478bd9Sstevel@tonic-gate 
162*32885d59Sgtb extern uint_t kwarn_add_warning(char *, int);
163*32885d59Sgtb extern uint_t kwarn_del_warning(char *);
164*32885d59Sgtb 
1657c478bd9Sstevel@tonic-gate static boolean_t auth_debug = 0;
1667c478bd9Sstevel@tonic-gate static boolean_t negotiate_auth_krb5 = 1;
1677c478bd9Sstevel@tonic-gate static boolean_t auth_negotiated = 0;
1687c478bd9Sstevel@tonic-gate static int auth_status = 0;
1697c478bd9Sstevel@tonic-gate static int auth_level = 0;
1707c478bd9Sstevel@tonic-gate static char	*AuthenticatingUser = NULL;
1717c478bd9Sstevel@tonic-gate static char	*krb5_name = NULL;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate static krb5_address rsaddr = { 0, 0, 0, NULL };
1747c478bd9Sstevel@tonic-gate static krb5_address rsport = { 0, 0, 0, NULL };
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate static krb5_context telnet_context = 0;
1777c478bd9Sstevel@tonic-gate static krb5_auth_context auth_context = 0;
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate /* telnetd gets session key from here */
1807c478bd9Sstevel@tonic-gate static krb5_ticket *ticket = NULL;
1817c478bd9Sstevel@tonic-gate static krb5_keyblock *session_key = NULL;
1827c478bd9Sstevel@tonic-gate static char *telnet_srvtab = NULL;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate typedef struct {
1857c478bd9Sstevel@tonic-gate 	uchar_t AuthName;
1867c478bd9Sstevel@tonic-gate 	uchar_t AuthHow;
1877c478bd9Sstevel@tonic-gate 	char  *AuthString;
1887c478bd9Sstevel@tonic-gate } AuthInfo;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate static AuthInfo auth_list[] = {
1917c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
1927c478bd9Sstevel@tonic-gate 	AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
1937c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
1947c478bd9Sstevel@tonic-gate 	"KRB5 MUTUAL" },
1957c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5,	AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
1967c478bd9Sstevel@tonic-gate 	"KRB5 1-WAY" },
1977c478bd9Sstevel@tonic-gate 	{0, 0, "NONE"}
1987c478bd9Sstevel@tonic-gate };
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate static AuthInfo NoAuth = {0, 0, NULL};
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate static AuthInfo *authenticated = NULL;
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate #define	PREAMBLE_SIZE		5	/* for auth_reply_str allocation */
2057c478bd9Sstevel@tonic-gate #define	POSTAMBLE_SIZE		5
2067c478bd9Sstevel@tonic-gate #define	STR_DATA_LEN(len)	((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate static void auth_name(uchar_t *, int);
2097c478bd9Sstevel@tonic-gate static void auth_is(uchar_t *, int);
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate #define	NO_ENCRYPTION   0x00
2127c478bd9Sstevel@tonic-gate #define	SEND_ENCRYPTED  0x01
2137c478bd9Sstevel@tonic-gate #define	RECV_ENCRYPTED  0x02
2147c478bd9Sstevel@tonic-gate #define	ENCRYPT_BOTH_WAYS    (SEND_ENCRYPTED | RECV_ENCRYPTED)
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate static telnet_enc_data_t  encr_data;
2177c478bd9Sstevel@tonic-gate static boolean_t negotiate_encrypt = B_TRUE;
2187c478bd9Sstevel@tonic-gate static boolean_t sent_encrypt_support = B_FALSE;
2197c478bd9Sstevel@tonic-gate static boolean_t sent_will_encrypt = B_FALSE;
2207c478bd9Sstevel@tonic-gate static boolean_t sent_do_encrypt = B_FALSE;
2217c478bd9Sstevel@tonic-gate static boolean_t enc_debug = 0;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
2247c478bd9Sstevel@tonic-gate static int  encrypt_send_encrypt_is();
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate extern void mit_des_fixup_key_parity(Block);
2277c478bd9Sstevel@tonic-gate extern int krb5_setenv(const char *, const char *, int);
2287c478bd9Sstevel@tonic-gate /* need to know what FD to use to talk to the crypto module */
2297c478bd9Sstevel@tonic-gate static int cryptmod_fd = -1;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate #define	LOGIN_PROGRAM "/bin/login"
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate /*
2347c478bd9Sstevel@tonic-gate  * State for recv fsm
2357c478bd9Sstevel@tonic-gate  */
2367c478bd9Sstevel@tonic-gate #define	TS_DATA		0	/* base state */
2377c478bd9Sstevel@tonic-gate #define	TS_IAC		1	/* look for double IAC's */
2387c478bd9Sstevel@tonic-gate #define	TS_CR		2	/* CR-LF ->'s CR */
2397c478bd9Sstevel@tonic-gate #define	TS_SB		3	/* throw away begin's... */
2407c478bd9Sstevel@tonic-gate #define	TS_SE		4	/* ...end's (suboption negotiation) */
2417c478bd9Sstevel@tonic-gate #define	TS_WILL		5	/* will option negotiation */
2427c478bd9Sstevel@tonic-gate #define	TS_WONT		6	/* wont " */
2437c478bd9Sstevel@tonic-gate #define	TS_DO		7	/* do " */
2447c478bd9Sstevel@tonic-gate #define	TS_DONT		8	/* dont " */
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate static int	ncc;
2477c478bd9Sstevel@tonic-gate static int	master;		/* master side of pty */
2487c478bd9Sstevel@tonic-gate static int	pty;		/* side of pty that gets ioctls */
2497c478bd9Sstevel@tonic-gate static int	net;
2507c478bd9Sstevel@tonic-gate static int	inter;
2517c478bd9Sstevel@tonic-gate extern char **environ;
2527c478bd9Sstevel@tonic-gate static char	*line;
2537c478bd9Sstevel@tonic-gate static int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
2547c478bd9Sstevel@tonic-gate static int	state = TS_DATA;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate static int env_ovar = -1;	/* XXX.sparker */
2577c478bd9Sstevel@tonic-gate static int env_ovalue = -1;	/* XXX.sparker */
2587c478bd9Sstevel@tonic-gate static char pam_svc_name[64];
2597c478bd9Sstevel@tonic-gate static boolean_t	telmod_init_done = B_FALSE;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate static void	doit(int, struct sockaddr_storage *);
2627c478bd9Sstevel@tonic-gate static void	willoption(int);
2637c478bd9Sstevel@tonic-gate static void	wontoption(int);
2647c478bd9Sstevel@tonic-gate static void	dooption(int);
2657c478bd9Sstevel@tonic-gate static void	dontoption(int);
2667c478bd9Sstevel@tonic-gate static void	fatal(int, char *);
2677c478bd9Sstevel@tonic-gate static void	fatalperror(int, char *, int);
2687c478bd9Sstevel@tonic-gate static void	mode(int, int);
2697c478bd9Sstevel@tonic-gate static void	interrupt(void);
2707c478bd9Sstevel@tonic-gate static void	drainstream(int);
2717c478bd9Sstevel@tonic-gate static int	readstream(int, char *, int);
2727c478bd9Sstevel@tonic-gate static int	send_oob(int fd, char *ptr, int count);
2737c478bd9Sstevel@tonic-gate static int	local_setenv(const char *name, const char *value, int rewrite);
2747c478bd9Sstevel@tonic-gate static void	local_unsetenv(const char *name);
2757c478bd9Sstevel@tonic-gate static void	suboption(void);
2767c478bd9Sstevel@tonic-gate static int	removemod(int f, char *modname);
2777c478bd9Sstevel@tonic-gate static void	willoption(int option);
2787c478bd9Sstevel@tonic-gate static void	wontoption(int option);
2797c478bd9Sstevel@tonic-gate static void	dooption(int option);
2807c478bd9Sstevel@tonic-gate static void	dontoption(int option);
2817c478bd9Sstevel@tonic-gate static void	write_data(const char *, ...);
2827c478bd9Sstevel@tonic-gate static void	write_data_len(const char *, int);
2837c478bd9Sstevel@tonic-gate static void	rmut(void);
2847c478bd9Sstevel@tonic-gate static void	cleanup(int);
2857c478bd9Sstevel@tonic-gate static void	telnet(int, int);
2867c478bd9Sstevel@tonic-gate static void	telrcv(void);
2877c478bd9Sstevel@tonic-gate static void	sendbrk(void);
2887c478bd9Sstevel@tonic-gate static void	ptyflush(void);
2897c478bd9Sstevel@tonic-gate static void	netclear(void);
2907c478bd9Sstevel@tonic-gate static void	netflush(void);
2917c478bd9Sstevel@tonic-gate static void	showbanner(void);
2927c478bd9Sstevel@tonic-gate static void	map_banner(char *);
2937c478bd9Sstevel@tonic-gate static void	defbanner(void);
2947c478bd9Sstevel@tonic-gate static void ttloop(void);
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate  * The env_list linked list is used to store the environment variables
2987c478bd9Sstevel@tonic-gate  * until the final exec of login.  A malevolent client might try to
2997c478bd9Sstevel@tonic-gate  * send an environment variable intended to affect the telnet daemon's
3007c478bd9Sstevel@tonic-gate  * execution.  Right now the BANNER expansion is the only instance.
3017c478bd9Sstevel@tonic-gate  * Note that it is okay to pass the environment variables to login
3027c478bd9Sstevel@tonic-gate  * because login protects itself against environment variables mischief.
3037c478bd9Sstevel@tonic-gate  */
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate struct envlist {
3067c478bd9Sstevel@tonic-gate 	struct envlist	*next;
3077c478bd9Sstevel@tonic-gate 	char		*name;
3087c478bd9Sstevel@tonic-gate 	char		*value;
3097c478bd9Sstevel@tonic-gate 	int		delete;
3107c478bd9Sstevel@tonic-gate };
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate static struct envlist *envlist_head = NULL;
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate /*
3157c478bd9Sstevel@tonic-gate  * The following are some clocks used to decide how to interpret
3167c478bd9Sstevel@tonic-gate  * the relationship between various variables.
3177c478bd9Sstevel@tonic-gate  */
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate static struct {
3207c478bd9Sstevel@tonic-gate 	int
3217c478bd9Sstevel@tonic-gate 	system,			/* what the current time is */
3227c478bd9Sstevel@tonic-gate 	echotoggle,		/* last time user entered echo character */
3237c478bd9Sstevel@tonic-gate 	modenegotiated,		/* last time operating mode negotiated */
3247c478bd9Sstevel@tonic-gate 	didnetreceive,		/* last time we read data from network */
3257c478bd9Sstevel@tonic-gate 	ttypeopt,		/* ttype will/won't received */
3267c478bd9Sstevel@tonic-gate 	ttypesubopt,		/* ttype subopt is received */
3277c478bd9Sstevel@tonic-gate 	getterminal,		/* time started to get terminal information */
3287c478bd9Sstevel@tonic-gate 	xdisplocopt,		/* xdisploc will/wont received */
3297c478bd9Sstevel@tonic-gate 	xdisplocsubopt,		/* xdisploc suboption received */
3307c478bd9Sstevel@tonic-gate 	nawsopt,		/* window size will/wont received */
3317c478bd9Sstevel@tonic-gate 	nawssubopt,		/* window size received */
3327c478bd9Sstevel@tonic-gate 	environopt,		/* environment option will/wont received */
3337c478bd9Sstevel@tonic-gate 	oenvironopt,		/* "old" environ option will/wont received */
3347c478bd9Sstevel@tonic-gate 	environsubopt,		/* environment option suboption received */
3357c478bd9Sstevel@tonic-gate 	oenvironsubopt,		/* "old environ option suboption received */
3367c478bd9Sstevel@tonic-gate 	gotDM;			/* when did we last see a data mark */
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	int getauth;
3397c478bd9Sstevel@tonic-gate 	int authopt;	/* Authentication option negotiated */
3407c478bd9Sstevel@tonic-gate 	int authdone;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	int getencr;
3437c478bd9Sstevel@tonic-gate 	int encropt;
3447c478bd9Sstevel@tonic-gate 	int encr_support;
3457c478bd9Sstevel@tonic-gate } clocks;
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate static int init_neg_done = 0;
3487c478bd9Sstevel@tonic-gate static boolean_t resolve_hostname = 0;
3497c478bd9Sstevel@tonic-gate static boolean_t show_hostinfo = 1;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate #define	settimer(x)	(clocks.x = ++clocks.system)
3527c478bd9Sstevel@tonic-gate #define	sequenceIs(x, y)	(clocks.x < clocks.y)
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate static void send_will(int);
3557c478bd9Sstevel@tonic-gate static void send_wont(int);
3567c478bd9Sstevel@tonic-gate static void send_do(int);
3577c478bd9Sstevel@tonic-gate static char *__findenv(const char *name, int *offset);
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate /* ARGSUSED */
3607c478bd9Sstevel@tonic-gate static void
3617c478bd9Sstevel@tonic-gate auth_finished(AuthInfo *ap, int result)
3627c478bd9Sstevel@tonic-gate {
3637c478bd9Sstevel@tonic-gate 	if ((authenticated = ap) == NULL) {
3647c478bd9Sstevel@tonic-gate 		authenticated = &NoAuth;
3657c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ENCRYPT] == OPT_YES)
3667c478bd9Sstevel@tonic-gate 			send_wont(TELOPT_ENCRYPT);
3677c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
3687c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 0;
3697c478bd9Sstevel@tonic-gate 	} else if (result != AUTH_REJECT &&
3707c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] == OPT_YES &&
3717c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] == OPT_YES) {
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 		/*
3747c478bd9Sstevel@tonic-gate 		 * Authentication successful, so we have a session key, and
3757c478bd9Sstevel@tonic-gate 		 * we're willing to do ENCRYPT, so send our ENCRYPT SUPPORT.
3767c478bd9Sstevel@tonic-gate 		 *
3777c478bd9Sstevel@tonic-gate 		 * Can't have sent ENCRYPT SUPPORT yet!  And if we're sending it
3787c478bd9Sstevel@tonic-gate 		 * now it's really only because we did the DO ENCRYPT/WILL
3797c478bd9Sstevel@tonic-gate 		 * ENCRYPT dance before authentication, which is ok, but not too
3807c478bd9Sstevel@tonic-gate 		 * bright since we have to do the DONT ENCRYPT/WONT ENCRYPT
3817c478bd9Sstevel@tonic-gate 		 * dance if authentication fails, though clients typically just
3827c478bd9Sstevel@tonic-gate 		 * don't care.
3837c478bd9Sstevel@tonic-gate 		 */
3847c478bd9Sstevel@tonic-gate 		write_data("%c%c%c%c%c%c%c",
3857c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
3867c478bd9Sstevel@tonic-gate 			(uchar_t)SB,
3877c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCRYPT,
3887c478bd9Sstevel@tonic-gate 			(uchar_t)ENCRYPT_SUPPORT,
3897c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
3907c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
3917c478bd9Sstevel@tonic-gate 			(uchar_t)SE);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 		netflush();
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 		sent_encrypt_support = B_TRUE;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 		if (enc_debug)
3987c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3997c478bd9Sstevel@tonic-gate 			"SENT ENCRYPT SUPPORT\n");
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 		(void) encrypt_send_encrypt_is();
4027c478bd9Sstevel@tonic-gate 	}
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	auth_status = result;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	settimer(authdone);
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate static void
4107c478bd9Sstevel@tonic-gate reply_to_client(AuthInfo *ap, int type, void *data, int len)
4117c478bd9Sstevel@tonic-gate {
4127c478bd9Sstevel@tonic-gate 	uchar_t reply[BUFSIZ];
4137c478bd9Sstevel@tonic-gate 	uchar_t *p = reply;
4147c478bd9Sstevel@tonic-gate 	uchar_t *cd = (uchar_t *)data;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	if (len == -1 && data != NULL)
4177c478bd9Sstevel@tonic-gate 		len = strlen((char *)data);
4187c478bd9Sstevel@tonic-gate 	else if (len > (sizeof (reply) - 9)) {
4197c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
4207c478bd9Sstevel@tonic-gate 		    "krb5 auth reply length too large (%d)", len);
4217c478bd9Sstevel@tonic-gate 		if (auth_debug)
4227c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
4237c478bd9Sstevel@tonic-gate 				    "krb5 auth reply length too large (%d)\n",
4247c478bd9Sstevel@tonic-gate 				    len);
4257c478bd9Sstevel@tonic-gate 		return;
4267c478bd9Sstevel@tonic-gate 	} else if (data == NULL)
4277c478bd9Sstevel@tonic-gate 		len = 0;
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	*p++ = IAC;
4307c478bd9Sstevel@tonic-gate 	*p++ = SB;
4317c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_AUTHENTICATION;
4327c478bd9Sstevel@tonic-gate 	*p++ = AUTHTYPE_KERBEROS_V5;
4337c478bd9Sstevel@tonic-gate 	*p++ = ap->AuthName;
4347c478bd9Sstevel@tonic-gate 	*p++ = ap->AuthHow; /* MUTUAL, ONE-WAY, etc */
4357c478bd9Sstevel@tonic-gate 	*p++ = type;	    /* RESPONSE or ACCEPT */
4367c478bd9Sstevel@tonic-gate 	while (len-- > 0) {
4377c478bd9Sstevel@tonic-gate 		if ((*p++ = *cd++) == IAC)
4387c478bd9Sstevel@tonic-gate 			*p++ = IAC;
4397c478bd9Sstevel@tonic-gate 	}
4407c478bd9Sstevel@tonic-gate 	*p++ = IAC;
4417c478bd9Sstevel@tonic-gate 	*p++ = SE;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	/* queue the data to be sent */
4447c478bd9Sstevel@tonic-gate 	write_data_len((const char *)reply, p-reply);
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate #if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
4477c478bd9Sstevel@tonic-gate defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
4487c478bd9Sstevel@tonic-gate 	if (auth_debug) {
4497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
4507c478bd9Sstevel@tonic-gate 			    "%s %s|%s %s\n",
4517c478bd9Sstevel@tonic-gate 			    AUTHTYPE_NAME(ap->AuthName),
4527c478bd9Sstevel@tonic-gate 			    AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
4537c478bd9Sstevel@tonic-gate 			    AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
4547c478bd9Sstevel@tonic-gate 			    AUTHRSP_NAME(type));
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate #endif /* AUTHTYPE_NAMES && AUTHWHO_NAMES && AUTHHOW_NAMES && AUTHRSP_NAMES */
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	netflush();
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate /* Decode, decrypt and store the forwarded creds in the local ccache. */
4627c478bd9Sstevel@tonic-gate static krb5_error_code
4637c478bd9Sstevel@tonic-gate rd_and_store_forwarded_creds(krb5_context context,
4647c478bd9Sstevel@tonic-gate 			    krb5_auth_context auth_context,
4657c478bd9Sstevel@tonic-gate 			    krb5_data *inbuf, krb5_ticket *ticket,
4667c478bd9Sstevel@tonic-gate 			    char *username)
4677c478bd9Sstevel@tonic-gate {
4687c478bd9Sstevel@tonic-gate 	krb5_creds **creds;
4697c478bd9Sstevel@tonic-gate 	krb5_error_code retval;
470*32885d59Sgtb 	char ccname[MAXPATHLEN];
4717c478bd9Sstevel@tonic-gate 	krb5_ccache ccache = NULL;
472*32885d59Sgtb 	char *client_name = NULL;
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
4757c478bd9Sstevel@tonic-gate 		return (retval);
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	(void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
4787c478bd9Sstevel@tonic-gate 	(void) krb5_setenv("KRB5CCNAME", ccname, 1);
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_default(context, &ccache)))
4817c478bd9Sstevel@tonic-gate 		goto cleanup;
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_initialize(context, ccache,
4847c478bd9Sstevel@tonic-gate 					ticket->enc_part2->client)) != 0)
4857c478bd9Sstevel@tonic-gate 		goto cleanup;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
4887c478bd9Sstevel@tonic-gate 		goto cleanup;
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_close(context, ccache)) != 0)
4917c478bd9Sstevel@tonic-gate 		goto cleanup;
4927c478bd9Sstevel@tonic-gate 
493*32885d59Sgtb 	/* Register with ktkt_warnd(1M) */
494*32885d59Sgtb 	if ((retval = krb5_unparse_name(context, (*creds)->client,
495*32885d59Sgtb 					&client_name)) != 0)
496*32885d59Sgtb 		goto cleanup;
497*32885d59Sgtb 	(void) kwarn_del_warning(client_name);
498*32885d59Sgtb 	if (kwarn_add_warning(client_name, (*creds)->times.endtime) != 0) {
499*32885d59Sgtb 		syslog(LOG_AUTH|LOG_NOTICE,
500*32885d59Sgtb 		    "rd_and_store_forwarded_creds: kwarn_add_warning"
501*32885d59Sgtb 		    " failed: ktkt_warnd(1M) down? ");
502*32885d59Sgtb 		if (auth_debug)
503*32885d59Sgtb 			(void) fprintf(stderr,
504*32885d59Sgtb 				    "kwarn_add_warning failed:"
505*32885d59Sgtb 				    " ktkt_warnd(1M) down?\n");
506*32885d59Sgtb 	}
507*32885d59Sgtb 	free(client_name);
508*32885d59Sgtb 	client_name = NULL;
509*32885d59Sgtb 
5107c478bd9Sstevel@tonic-gate 	if (username != NULL) {
5117c478bd9Sstevel@tonic-gate 		/*
5127c478bd9Sstevel@tonic-gate 		 * This verifies that the user is valid on the local system,
5137c478bd9Sstevel@tonic-gate 		 * maps the username from KerberosV5 to unix,
5147c478bd9Sstevel@tonic-gate 		 * and moves the KRB5CCNAME file to the correct place
5157c478bd9Sstevel@tonic-gate 		 *  /tmp/krb5cc_[uid] with correct ownership (0600 uid gid).
5167c478bd9Sstevel@tonic-gate 		 *
5177c478bd9Sstevel@tonic-gate 		 * NOTE: the user must be in the gsscred table in order to map
5187c478bd9Sstevel@tonic-gate 		 * from KRB5 to Unix.
5197c478bd9Sstevel@tonic-gate 		 */
5207c478bd9Sstevel@tonic-gate 		(void) krb5_kuserok(context, ticket->enc_part2->client,
5217c478bd9Sstevel@tonic-gate 				username);
5227c478bd9Sstevel@tonic-gate 	}
5237c478bd9Sstevel@tonic-gate 	if (auth_debug)
5247c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
5257c478bd9Sstevel@tonic-gate 			    "Successfully stored forwarded creds\n");
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate cleanup:
5287c478bd9Sstevel@tonic-gate 	krb5_free_creds(context, *creds);
5297c478bd9Sstevel@tonic-gate 	return (retval);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate static void
5337c478bd9Sstevel@tonic-gate kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
5347c478bd9Sstevel@tonic-gate {
5357c478bd9Sstevel@tonic-gate 	krb5_error_code err = 0;
5367c478bd9Sstevel@tonic-gate 	krb5_principal server;
5377c478bd9Sstevel@tonic-gate 	krb5_keyblock *newkey = NULL;
5387c478bd9Sstevel@tonic-gate 	krb5_keytab keytabid = 0;
5397c478bd9Sstevel@tonic-gate 	krb5_data outbuf;
5407c478bd9Sstevel@tonic-gate 	krb5_data inbuf;
5417c478bd9Sstevel@tonic-gate 	krb5_authenticator *authenticator;
5427c478bd9Sstevel@tonic-gate 	char errbuf[MAXERRSTRLEN];
5437c478bd9Sstevel@tonic-gate 	char *name;
5447c478bd9Sstevel@tonic-gate 	krb5_data auth;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	Session_Key skey;
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	if (cnt-- < 1)
5497c478bd9Sstevel@tonic-gate 		return;
5507c478bd9Sstevel@tonic-gate 	switch (*data++) {
5517c478bd9Sstevel@tonic-gate 	case KRB_AUTH:
5527c478bd9Sstevel@tonic-gate 		auth.data = (char *)data;
5537c478bd9Sstevel@tonic-gate 		auth.length = cnt;
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 		if (auth_context == NULL) {
5567c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_init(telnet_context, &auth_context);
5577c478bd9Sstevel@tonic-gate 			if (err)
5587c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
5597c478bd9Sstevel@tonic-gate 				    "Error getting krb5 auth "
5607c478bd9Sstevel@tonic-gate 				    "context: %s", error_message(err));
5617c478bd9Sstevel@tonic-gate 		}
5627c478bd9Sstevel@tonic-gate 		if (!err) {
5637c478bd9Sstevel@tonic-gate 			krb5_rcache rcache;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getrcache(telnet_context,
5667c478bd9Sstevel@tonic-gate 						    auth_context,
5677c478bd9Sstevel@tonic-gate 						    &rcache);
5687c478bd9Sstevel@tonic-gate 			if (!err && !rcache) {
5697c478bd9Sstevel@tonic-gate 				err = krb5_sname_to_principal(telnet_context,
5707c478bd9Sstevel@tonic-gate 							    0, 0,
5717c478bd9Sstevel@tonic-gate 							    KRB5_NT_SRV_HST,
5727c478bd9Sstevel@tonic-gate 							    &server);
5737c478bd9Sstevel@tonic-gate 				if (!err) {
5747c478bd9Sstevel@tonic-gate 					err = krb5_get_server_rcache(
5757c478bd9Sstevel@tonic-gate 						telnet_context,
5767c478bd9Sstevel@tonic-gate 						krb5_princ_component(
5777c478bd9Sstevel@tonic-gate 							telnet_context,
5787c478bd9Sstevel@tonic-gate 							server, 0),
5797c478bd9Sstevel@tonic-gate 						&rcache);
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 					krb5_free_principal(telnet_context,
5827c478bd9Sstevel@tonic-gate 							    server);
5837c478bd9Sstevel@tonic-gate 				}
5847c478bd9Sstevel@tonic-gate 			}
5857c478bd9Sstevel@tonic-gate 			if (err)
5867c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
5877c478bd9Sstevel@tonic-gate 				    "Error allocating krb5 replay cache: %s",
5887c478bd9Sstevel@tonic-gate 				    error_message(err));
5897c478bd9Sstevel@tonic-gate 			else {
5907c478bd9Sstevel@tonic-gate 				err = krb5_auth_con_setrcache(telnet_context,
5917c478bd9Sstevel@tonic-gate 							    auth_context,
5927c478bd9Sstevel@tonic-gate 							    rcache);
5937c478bd9Sstevel@tonic-gate 				if (err)
5947c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
5957c478bd9Sstevel@tonic-gate 					    "Error creating krb5 "
5967c478bd9Sstevel@tonic-gate 					    "replay cache: %s",
5977c478bd9Sstevel@tonic-gate 					    error_message(err));
5987c478bd9Sstevel@tonic-gate 			}
5997c478bd9Sstevel@tonic-gate 		}
6007c478bd9Sstevel@tonic-gate 		if (!err && telnet_srvtab != NULL)
6017c478bd9Sstevel@tonic-gate 			err = krb5_kt_resolve(telnet_context,
6027c478bd9Sstevel@tonic-gate 					    telnet_srvtab, &keytabid);
6037c478bd9Sstevel@tonic-gate 		if (!err)
6047c478bd9Sstevel@tonic-gate 			err = krb5_rd_req(telnet_context, &auth_context, &auth,
6057c478bd9Sstevel@tonic-gate 					NULL, keytabid, NULL, &ticket);
6067c478bd9Sstevel@tonic-gate 		if (err) {
6077c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
6087c478bd9Sstevel@tonic-gate 				"Error reading krb5 auth information:"
6097c478bd9Sstevel@tonic-gate 				" %s", error_message(err));
6107c478bd9Sstevel@tonic-gate 			goto errout;
6117c478bd9Sstevel@tonic-gate 		}
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 		/*
6147c478bd9Sstevel@tonic-gate 		 * Verify that the correct principal was used
6157c478bd9Sstevel@tonic-gate 		 */
6167c478bd9Sstevel@tonic-gate 		if (krb5_princ_component(telnet_context,
6177c478bd9Sstevel@tonic-gate 				ticket->server, 0)->length < MAXPRINCLEN) {
6187c478bd9Sstevel@tonic-gate 			char princ[MAXPRINCLEN];
6197c478bd9Sstevel@tonic-gate 			(void) strncpy(princ,
6207c478bd9Sstevel@tonic-gate 				    krb5_princ_component(telnet_context,
6217c478bd9Sstevel@tonic-gate 						ticket->server, 0)->data,
6227c478bd9Sstevel@tonic-gate 				    krb5_princ_component(telnet_context,
6237c478bd9Sstevel@tonic-gate 					    ticket->server, 0)->length);
6247c478bd9Sstevel@tonic-gate 			princ[krb5_princ_component(telnet_context,
6257c478bd9Sstevel@tonic-gate 					ticket->server, 0)->length] = '\0';
6267c478bd9Sstevel@tonic-gate 			if (strcmp("host", princ)) {
6277c478bd9Sstevel@tonic-gate 				if (strlen(princ) < sizeof (errbuf) - 39) {
6287c478bd9Sstevel@tonic-gate 				    (void) snprintf(errbuf, sizeof (errbuf),
6297c478bd9Sstevel@tonic-gate 						"incorrect service "
6307c478bd9Sstevel@tonic-gate 						    "name: \"%s\" != "
6317c478bd9Sstevel@tonic-gate 						    "\"host\"",
6327c478bd9Sstevel@tonic-gate 						    princ);
6337c478bd9Sstevel@tonic-gate 			    } else {
6347c478bd9Sstevel@tonic-gate 				    (void) strncpy(errbuf,
6357c478bd9Sstevel@tonic-gate 						"incorrect service "
6367c478bd9Sstevel@tonic-gate 						"name: principal != "
6377c478bd9Sstevel@tonic-gate 						"\"host\"",
6387c478bd9Sstevel@tonic-gate 						sizeof (errbuf));
6397c478bd9Sstevel@tonic-gate 			    }
6407c478bd9Sstevel@tonic-gate 			    goto errout;
6417c478bd9Sstevel@tonic-gate 			}
6427c478bd9Sstevel@tonic-gate 		} else {
6437c478bd9Sstevel@tonic-gate 			(void) strlcpy(errbuf, "service name too long",
6447c478bd9Sstevel@tonic-gate 					sizeof (errbuf));
6457c478bd9Sstevel@tonic-gate 			goto errout;
6467c478bd9Sstevel@tonic-gate 		}
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 		err = krb5_auth_con_getauthenticator(telnet_context,
6497c478bd9Sstevel@tonic-gate 						auth_context,
6507c478bd9Sstevel@tonic-gate 						&authenticator);
6517c478bd9Sstevel@tonic-gate 		if (err) {
6527c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
6537c478bd9Sstevel@tonic-gate 				"Failed to get authenticator: %s",
6547c478bd9Sstevel@tonic-gate 				error_message(err));
6557c478bd9Sstevel@tonic-gate 			goto errout;
6567c478bd9Sstevel@tonic-gate 		}
6577c478bd9Sstevel@tonic-gate 		if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
6587c478bd9Sstevel@tonic-gate 			!authenticator->checksum) {
6597c478bd9Sstevel@tonic-gate 			(void) strlcpy(errbuf,
6607c478bd9Sstevel@tonic-gate 				    "authenticator is missing checksum",
6617c478bd9Sstevel@tonic-gate 				    sizeof (errbuf));
6627c478bd9Sstevel@tonic-gate 			goto errout;
6637c478bd9Sstevel@tonic-gate 		}
6647c478bd9Sstevel@tonic-gate 		if (authenticator->checksum) {
6657c478bd9Sstevel@tonic-gate 			char type_check[2];
6667c478bd9Sstevel@tonic-gate 			krb5_checksum *cksum = authenticator->checksum;
6677c478bd9Sstevel@tonic-gate 			krb5_keyblock *key;
6687c478bd9Sstevel@tonic-gate 			krb5_data input;
6697c478bd9Sstevel@tonic-gate 			krb5_boolean valid;
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 			type_check[0] = ap->AuthName;
6727c478bd9Sstevel@tonic-gate 			type_check[1] = ap->AuthHow;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getkey(telnet_context,
6757c478bd9Sstevel@tonic-gate 						auth_context, &key);
6767c478bd9Sstevel@tonic-gate 			if (err) {
6777c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
6787c478bd9Sstevel@tonic-gate 					"Failed to get key from "
6797c478bd9Sstevel@tonic-gate 					"authenticator: %s",
6807c478bd9Sstevel@tonic-gate 					error_message(err));
6817c478bd9Sstevel@tonic-gate 				goto errout;
6827c478bd9Sstevel@tonic-gate 			}
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 			input.data = type_check;
6857c478bd9Sstevel@tonic-gate 			input.length = 2;
6867c478bd9Sstevel@tonic-gate 			err = krb5_c_verify_checksum(telnet_context,
6877c478bd9Sstevel@tonic-gate 							key, 0,
6887c478bd9Sstevel@tonic-gate 							&input,
6897c478bd9Sstevel@tonic-gate 							cksum,
6907c478bd9Sstevel@tonic-gate 							&valid);
6917c478bd9Sstevel@tonic-gate 			if (!err && !valid)
6927c478bd9Sstevel@tonic-gate 				err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 			if (err) {
6957c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
6967c478bd9Sstevel@tonic-gate 						"Kerberos checksum "
6977c478bd9Sstevel@tonic-gate 						"verification failed: "
6987c478bd9Sstevel@tonic-gate 						"%s",
6997c478bd9Sstevel@tonic-gate 						error_message(err));
7007c478bd9Sstevel@tonic-gate 				goto errout;
7017c478bd9Sstevel@tonic-gate 			}
7027c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, key);
7037c478bd9Sstevel@tonic-gate 		}
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 		krb5_free_authenticator(telnet_context, authenticator);
7067c478bd9Sstevel@tonic-gate 		if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
7077c478bd9Sstevel@tonic-gate 			/* do ap_rep stuff here */
7087c478bd9Sstevel@tonic-gate 			if ((err = krb5_mk_rep(telnet_context, auth_context,
7097c478bd9Sstevel@tonic-gate 					    &outbuf))) {
7107c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
7117c478bd9Sstevel@tonic-gate 						"Failed to make "
7127c478bd9Sstevel@tonic-gate 						"Kerberos auth reply: "
7137c478bd9Sstevel@tonic-gate 						"%s",
7147c478bd9Sstevel@tonic-gate 						error_message(err));
7157c478bd9Sstevel@tonic-gate 				goto errout;
7167c478bd9Sstevel@tonic-gate 			}
7177c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_RESPONSE, outbuf.data,
7187c478bd9Sstevel@tonic-gate 					outbuf.length);
7197c478bd9Sstevel@tonic-gate 		}
7207c478bd9Sstevel@tonic-gate 		if (krb5_unparse_name(telnet_context,
7217c478bd9Sstevel@tonic-gate 				    ticket->enc_part2->client,
7227c478bd9Sstevel@tonic-gate 				    &name))
7237c478bd9Sstevel@tonic-gate 			name = 0;
7247c478bd9Sstevel@tonic-gate 		reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
7257c478bd9Sstevel@tonic-gate 		if (auth_debug) {
7267c478bd9Sstevel@tonic-gate 			syslog(LOG_NOTICE,
7277c478bd9Sstevel@tonic-gate 			    "\tKerberos5 identifies user as ``%s''\r\n",
7287c478bd9Sstevel@tonic-gate 			    name ? name : "");
7297c478bd9Sstevel@tonic-gate 		}
7307c478bd9Sstevel@tonic-gate 		if (name != NULL) {
7317c478bd9Sstevel@tonic-gate 			krb5_name = (char *)strdup(name);
7327c478bd9Sstevel@tonic-gate 		}
7337c478bd9Sstevel@tonic-gate 		auth_finished(ap, AUTH_USER);
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 		if (name != NULL)
7367c478bd9Sstevel@tonic-gate 			free(name);
7377c478bd9Sstevel@tonic-gate 		krb5_auth_con_getremotesubkey(telnet_context, auth_context,
7387c478bd9Sstevel@tonic-gate 					    &newkey);
7397c478bd9Sstevel@tonic-gate 		if (session_key != NULL) {
7407c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, session_key);
7417c478bd9Sstevel@tonic-gate 			session_key = 0;
7427c478bd9Sstevel@tonic-gate 		}
7437c478bd9Sstevel@tonic-gate 		if (newkey != NULL) {
7447c478bd9Sstevel@tonic-gate 			krb5_copy_keyblock(telnet_context,
7457c478bd9Sstevel@tonic-gate 					newkey, &session_key);
7467c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, newkey);
7477c478bd9Sstevel@tonic-gate 		} else {
7487c478bd9Sstevel@tonic-gate 			krb5_copy_keyblock(telnet_context,
7497c478bd9Sstevel@tonic-gate 				ticket->enc_part2->session,
7507c478bd9Sstevel@tonic-gate 				&session_key);
7517c478bd9Sstevel@tonic-gate 		}
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 		/*
7547c478bd9Sstevel@tonic-gate 		 * Initialize encryption stuff.  Currently, we are only
7557c478bd9Sstevel@tonic-gate 		 * supporting 8 byte keys and blocks. Check for this later.
7567c478bd9Sstevel@tonic-gate 		 */
7577c478bd9Sstevel@tonic-gate 		skey.type = SK_DES;
7587c478bd9Sstevel@tonic-gate 		skey.length = DES_BLOCKSIZE;
7597c478bd9Sstevel@tonic-gate 		skey.data = session_key->contents;
7607c478bd9Sstevel@tonic-gate 		encrypt_session_key(&skey, &encr_data.encrypt);
7617c478bd9Sstevel@tonic-gate 		encrypt_session_key(&skey, &encr_data.decrypt);
7627c478bd9Sstevel@tonic-gate 		break;
7637c478bd9Sstevel@tonic-gate 	case KRB_FORWARD:
7647c478bd9Sstevel@tonic-gate 		inbuf.length = cnt;
7657c478bd9Sstevel@tonic-gate 		inbuf.data = (char *)data;
7667c478bd9Sstevel@tonic-gate 		if (auth_debug)
7677c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
7687c478bd9Sstevel@tonic-gate 				    "RCVD KRB_FORWARD data (%d bytes)\n", cnt);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 		if (auth_context != NULL) {
7717c478bd9Sstevel@tonic-gate 			krb5_rcache rcache;
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getrcache(telnet_context,
7747c478bd9Sstevel@tonic-gate 						    auth_context, &rcache);
7757c478bd9Sstevel@tonic-gate 			if (!err && !rcache) {
7767c478bd9Sstevel@tonic-gate 				err = krb5_sname_to_principal(telnet_context,
7777c478bd9Sstevel@tonic-gate 					0, 0, KRB5_NT_SRV_HST, &server);
7787c478bd9Sstevel@tonic-gate 				if (!err) {
7797c478bd9Sstevel@tonic-gate 					err = krb5_get_server_rcache(
7807c478bd9Sstevel@tonic-gate 						telnet_context,
7817c478bd9Sstevel@tonic-gate 						krb5_princ_component(
7827c478bd9Sstevel@tonic-gate 							telnet_context,
7837c478bd9Sstevel@tonic-gate 							server, 0),
7847c478bd9Sstevel@tonic-gate 						&rcache);
7857c478bd9Sstevel@tonic-gate 					krb5_free_principal(telnet_context,
7867c478bd9Sstevel@tonic-gate 								server);
7877c478bd9Sstevel@tonic-gate 				}
7887c478bd9Sstevel@tonic-gate 			}
7897c478bd9Sstevel@tonic-gate 			if (err) {
7907c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
7917c478bd9Sstevel@tonic-gate 				    "Error allocating krb5 replay cache: %s",
7927c478bd9Sstevel@tonic-gate 				    error_message(err));
7937c478bd9Sstevel@tonic-gate 			} else {
7947c478bd9Sstevel@tonic-gate 				err = krb5_auth_con_setrcache(telnet_context,
7957c478bd9Sstevel@tonic-gate 					auth_context, rcache);
7967c478bd9Sstevel@tonic-gate 				if (err)
7977c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
7987c478bd9Sstevel@tonic-gate 					    "Error creating krb5 replay cache:"
7997c478bd9Sstevel@tonic-gate 					    " %s",
8007c478bd9Sstevel@tonic-gate 					    error_message(err));
8017c478bd9Sstevel@tonic-gate 			}
8027c478bd9Sstevel@tonic-gate 		}
8037c478bd9Sstevel@tonic-gate 		/*
8047c478bd9Sstevel@tonic-gate 		 * Use the 'rsaddr' and 'rsport' (remote service addr/port)
8057c478bd9Sstevel@tonic-gate 		 * from the original connection.  This data is used to
8067c478bd9Sstevel@tonic-gate 		 * verify the forwarded credentials.
8077c478bd9Sstevel@tonic-gate 		 */
8087c478bd9Sstevel@tonic-gate 		if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
8097c478bd9Sstevel@tonic-gate 					    NULL, &rsaddr)))
8107c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_setports(telnet_context,
8117c478bd9Sstevel@tonic-gate 						auth_context, NULL, &rsport);
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 		if (err == 0)
8147c478bd9Sstevel@tonic-gate 			/*
8157c478bd9Sstevel@tonic-gate 			 * If all is well, store the forwarded creds in
8167c478bd9Sstevel@tonic-gate 			 * the users local credential cache.
8177c478bd9Sstevel@tonic-gate 			 */
8187c478bd9Sstevel@tonic-gate 			err = rd_and_store_forwarded_creds(telnet_context,
8197c478bd9Sstevel@tonic-gate 							auth_context, &inbuf,
8207c478bd9Sstevel@tonic-gate 							ticket,
8217c478bd9Sstevel@tonic-gate 							AuthenticatingUser);
8227c478bd9Sstevel@tonic-gate 		if (err) {
8237c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
8247c478bd9Sstevel@tonic-gate 					"Read forwarded creds failed: %s",
8257c478bd9Sstevel@tonic-gate 					error_message(err));
8267c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "%s", errbuf);
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
8297c478bd9Sstevel@tonic-gate 			if (auth_debug)
8307c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
8317c478bd9Sstevel@tonic-gate 					    "\tCould not read "
8327c478bd9Sstevel@tonic-gate 					    "forwarded credentials\r\n");
8337c478bd9Sstevel@tonic-gate 		} else
8347c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
8377c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
8407c478bd9Sstevel@tonic-gate 			free(rsport.contents);
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 		if (auth_debug)
8437c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\tForwarded "
8447c478bd9Sstevel@tonic-gate 						"credentials obtained\r\n");
8457c478bd9Sstevel@tonic-gate 		break;
8467c478bd9Sstevel@tonic-gate 	default:
8477c478bd9Sstevel@tonic-gate 		if (auth_debug)
8487c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
8497c478bd9Sstevel@tonic-gate 				    "\tUnknown Kerberos option %d\r\n",
8507c478bd9Sstevel@tonic-gate 				    data[-1]);
8517c478bd9Sstevel@tonic-gate 		reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
8527c478bd9Sstevel@tonic-gate 		break;
8537c478bd9Sstevel@tonic-gate 	}
8547c478bd9Sstevel@tonic-gate 	return;
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate errout:
8577c478bd9Sstevel@tonic-gate 	reply_to_client(ap, KRB_REJECT, errbuf, -1);
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	if (auth_debug)
8607c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "%s", errbuf);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	if (auth_context != NULL) {
8657c478bd9Sstevel@tonic-gate 		krb5_auth_con_free(telnet_context, auth_context);
8667c478bd9Sstevel@tonic-gate 		auth_context = 0;
8677c478bd9Sstevel@tonic-gate 	}
8687c478bd9Sstevel@tonic-gate }
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate static int
8717c478bd9Sstevel@tonic-gate krb5_init()
8727c478bd9Sstevel@tonic-gate {
8737c478bd9Sstevel@tonic-gate 	int code = 0;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (telnet_context == NULL) {
8767c478bd9Sstevel@tonic-gate 		code = krb5_init_context(&telnet_context);
8777c478bd9Sstevel@tonic-gate 		if (code != 0 && auth_debug)
8787c478bd9Sstevel@tonic-gate 			syslog(LOG_NOTICE,
8797c478bd9Sstevel@tonic-gate 			    "Cannot initialize Kerberos V5: %s",
8807c478bd9Sstevel@tonic-gate 			    error_message(code));
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	return (code);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate static void
8877c478bd9Sstevel@tonic-gate auth_name(uchar_t *data, int cnt)
8887c478bd9Sstevel@tonic-gate {
8897c478bd9Sstevel@tonic-gate 	char namebuf[MAXPRINCLEN];
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 	if (cnt < 1) {
8927c478bd9Sstevel@tonic-gate 		if (auth_debug)
8937c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
8947c478bd9Sstevel@tonic-gate 				    "\t(auth_name) Empty NAME in auth "
8957c478bd9Sstevel@tonic-gate 				    "reply\n");
8967c478bd9Sstevel@tonic-gate 		return;
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 	if (cnt > sizeof (namebuf)-1) {
8997c478bd9Sstevel@tonic-gate 		if (auth_debug)
9007c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
9017c478bd9Sstevel@tonic-gate 				    "\t(auth_name) NAME exceeds %d bytes\n",
9027c478bd9Sstevel@tonic-gate 				sizeof (namebuf)-1);
9037c478bd9Sstevel@tonic-gate 		return;
9047c478bd9Sstevel@tonic-gate 	}
9057c478bd9Sstevel@tonic-gate 	(void) memcpy((void *)namebuf, (void *)data, cnt);
9067c478bd9Sstevel@tonic-gate 	namebuf[cnt] = 0;
9077c478bd9Sstevel@tonic-gate 	if (auth_debug)
9087c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
9097c478bd9Sstevel@tonic-gate 	AuthenticatingUser = (char *)strdup(namebuf);
9107c478bd9Sstevel@tonic-gate }
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate static void
9137c478bd9Sstevel@tonic-gate auth_is(uchar_t *data, int cnt)
9147c478bd9Sstevel@tonic-gate {
9157c478bd9Sstevel@tonic-gate 	AuthInfo *aptr = auth_list;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	if (cnt < 2)
9187c478bd9Sstevel@tonic-gate 		return;
9197c478bd9Sstevel@tonic-gate 
9207c478bd9Sstevel@tonic-gate 	/*
9217c478bd9Sstevel@tonic-gate 	 * We failed to negoiate secure authentication
9227c478bd9Sstevel@tonic-gate 	 */
9237c478bd9Sstevel@tonic-gate 	if (data[0] == AUTHTYPE_NULL) {
9247c478bd9Sstevel@tonic-gate 		auth_finished(0, AUTH_REJECT);
9257c478bd9Sstevel@tonic-gate 		return;
9267c478bd9Sstevel@tonic-gate 	}
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	while (aptr->AuthName != NULL &&
9297c478bd9Sstevel@tonic-gate 	    (aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
9307c478bd9Sstevel@tonic-gate 		aptr++;
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 	if (aptr != NULL) {
9337c478bd9Sstevel@tonic-gate 		if (auth_debug)
9347c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\t(auth_is) auth type is %s "
9357c478bd9Sstevel@tonic-gate 				"(%d bytes)\n",	aptr->AuthString, cnt);
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 		if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
9387c478bd9Sstevel@tonic-gate 			kerberos5_is(aptr, data+2, cnt-2);
9397c478bd9Sstevel@tonic-gate 	}
9407c478bd9Sstevel@tonic-gate }
9417c478bd9Sstevel@tonic-gate 
9427c478bd9Sstevel@tonic-gate static int
9437c478bd9Sstevel@tonic-gate krb5_user_status(char *name, int namelen, int level)
9447c478bd9Sstevel@tonic-gate {
9457c478bd9Sstevel@tonic-gate 	int retval = AUTH_USER;
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 	if (auth_debug)
9487c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(krb5_user_status) level = %d "
9497c478bd9Sstevel@tonic-gate 			"auth_level = %d  user = %s\n",
9507c478bd9Sstevel@tonic-gate 			level, auth_level,
9517c478bd9Sstevel@tonic-gate 			(AuthenticatingUser != NULL ? AuthenticatingUser : ""));
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 	if (level < AUTH_USER)
9547c478bd9Sstevel@tonic-gate 		return (level);
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 	if (AuthenticatingUser != NULL &&
9577c478bd9Sstevel@tonic-gate 	    (retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
9587c478bd9Sstevel@tonic-gate 			    AuthenticatingUser))) {
9597c478bd9Sstevel@tonic-gate 		(void) strncpy(name, AuthenticatingUser, namelen);
9607c478bd9Sstevel@tonic-gate 		return (AUTH_VALID);
9617c478bd9Sstevel@tonic-gate 	} else {
9627c478bd9Sstevel@tonic-gate 		if (!retval)
9637c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR,
9647c478bd9Sstevel@tonic-gate 			    "Krb5 principal lacks permission to "
9657c478bd9Sstevel@tonic-gate 			    "access local account for %s",
9667c478bd9Sstevel@tonic-gate 			    AuthenticatingUser);
9677c478bd9Sstevel@tonic-gate 		return (AUTH_USER);
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate }
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate /*
9727c478bd9Sstevel@tonic-gate  * Wrapper around /dev/urandom
9737c478bd9Sstevel@tonic-gate  */
9747c478bd9Sstevel@tonic-gate static int
9757c478bd9Sstevel@tonic-gate getrandom(char *buf, int buflen)
9767c478bd9Sstevel@tonic-gate {
9777c478bd9Sstevel@tonic-gate 	static int devrandom = -1;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	if (devrandom == -1 &&
9807c478bd9Sstevel@tonic-gate 	    (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
9817c478bd9Sstevel@tonic-gate 		fatalperror(net, "Unable to open /dev/urandom: ",
9827c478bd9Sstevel@tonic-gate 			    errno);
9837c478bd9Sstevel@tonic-gate 		return (-1);
9847c478bd9Sstevel@tonic-gate 	}
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	if (read(devrandom, buf, buflen) == -1) {
9877c478bd9Sstevel@tonic-gate 		fatalperror(net, "Unable to read from /dev/urandom: ",
9887c478bd9Sstevel@tonic-gate 			    errno);
9897c478bd9Sstevel@tonic-gate 		return (-1);
9907c478bd9Sstevel@tonic-gate 	}
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	return (0);
9937c478bd9Sstevel@tonic-gate }
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate /*
9967c478bd9Sstevel@tonic-gate  * encrypt_init
9977c478bd9Sstevel@tonic-gate  *
9987c478bd9Sstevel@tonic-gate  * Initialize the encryption data structures
9997c478bd9Sstevel@tonic-gate  */
10007c478bd9Sstevel@tonic-gate static void
10017c478bd9Sstevel@tonic-gate encrypt_init()
10027c478bd9Sstevel@tonic-gate {
10037c478bd9Sstevel@tonic-gate 	(void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
10047c478bd9Sstevel@tonic-gate 	(void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = ENCR_STATE_NOT_READY;
10077c478bd9Sstevel@tonic-gate 	encr_data.decrypt.state = ENCR_STATE_NOT_READY;
10087c478bd9Sstevel@tonic-gate }
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate /*
10117c478bd9Sstevel@tonic-gate  * encrypt_send_request_start
10127c478bd9Sstevel@tonic-gate  *
10137c478bd9Sstevel@tonic-gate  * Request that the remote side automatically start sending
10147c478bd9Sstevel@tonic-gate  * encrypted output
10157c478bd9Sstevel@tonic-gate  */
10167c478bd9Sstevel@tonic-gate static void
10177c478bd9Sstevel@tonic-gate encrypt_send_request_start()
10187c478bd9Sstevel@tonic-gate {
10197c478bd9Sstevel@tonic-gate 	uchar_t buf[6+TELNET_MAXKEYIDLEN], *p;
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 	p = buf;
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 	*p++ = IAC;
10247c478bd9Sstevel@tonic-gate 	*p++ = SB;
10257c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
10267c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_REQSTART;
10277c478bd9Sstevel@tonic-gate 	/*
10287c478bd9Sstevel@tonic-gate 	 * We are telling the remote side which
10297c478bd9Sstevel@tonic-gate 	 * decrypt key we will use so that it may
10307c478bd9Sstevel@tonic-gate 	 * encrypt in the same key.
10317c478bd9Sstevel@tonic-gate 	 */
10327c478bd9Sstevel@tonic-gate 	(void) memcpy(p, encr_data.decrypt.keyid, encr_data.decrypt.keyidlen);
10337c478bd9Sstevel@tonic-gate 	p += encr_data.decrypt.keyidlen;
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 	*p++ = IAC;
10367c478bd9Sstevel@tonic-gate 	*p++ = SE;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	write_data_len((const char *)buf, p-buf);
10397c478bd9Sstevel@tonic-gate 	netflush();
10407c478bd9Sstevel@tonic-gate 	if (enc_debug)
10417c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
10427c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_REQSTART\n");
10437c478bd9Sstevel@tonic-gate }
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate /*
10467c478bd9Sstevel@tonic-gate  * encrypt_is
10477c478bd9Sstevel@tonic-gate  *
10487c478bd9Sstevel@tonic-gate  * When we receive the TELOPT_ENCRYPT ENCRYPT_IS ...
10497c478bd9Sstevel@tonic-gate  * message, the client is telling us that it will be sending
10507c478bd9Sstevel@tonic-gate  * encrypted data using the indicated cipher.
10517c478bd9Sstevel@tonic-gate  * We must initialize the read (decrypt) side of our connection
10527c478bd9Sstevel@tonic-gate  */
10537c478bd9Sstevel@tonic-gate static void
10547c478bd9Sstevel@tonic-gate encrypt_is(uchar_t *data, int cnt)
10557c478bd9Sstevel@tonic-gate {
10567c478bd9Sstevel@tonic-gate 	register int type;
10577c478bd9Sstevel@tonic-gate 	register int iv_status = CFB64_IV_OK;
10587c478bd9Sstevel@tonic-gate 	register int lstate = 0;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[] = {
10617c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
10627c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
10637c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
10647c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_REPLY,
10657c478bd9Sstevel@tonic-gate 		(uchar_t)0,		/* placeholder:  sbbuf[4] */
10667c478bd9Sstevel@tonic-gate 		(uchar_t)CFB64_IV_OK,	/* placeholder:  sbbuf[5] */
10677c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
10687c478bd9Sstevel@tonic-gate 		(uchar_t)SE,
10697c478bd9Sstevel@tonic-gate 	};
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 	if (--cnt < 0)
10727c478bd9Sstevel@tonic-gate 		return;
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate 	type = sbbuf[4] = *data++;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	/*
10777c478bd9Sstevel@tonic-gate 	 * Steps to take:
10787c478bd9Sstevel@tonic-gate 	 *   1. Create the proper stream Initialization vector
10797c478bd9Sstevel@tonic-gate 	 *		- copy the correct 'seed' to IV and output blocks
10807c478bd9Sstevel@tonic-gate 	 *		- set the correct key schedule
10817c478bd9Sstevel@tonic-gate 	 *   2. Generate reply for the other side:
10827c478bd9Sstevel@tonic-gate 	 *		IAC SB TELOPT_ENCRYPT ENCRYPT_REPLY type CFB64_IV_OK
10837c478bd9Sstevel@tonic-gate 	 *		[ data ... ] IAC SE
10847c478bd9Sstevel@tonic-gate 	 *   3. Tell crypto module:  method, direction, IV
10857c478bd9Sstevel@tonic-gate 	 */
10867c478bd9Sstevel@tonic-gate 	switch (type) {
10877c478bd9Sstevel@tonic-gate 	case TELOPT_ENCTYPE_DES_CFB64:
10887c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = type;
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 		lstate = encr_data.decrypt.state;
10917c478bd9Sstevel@tonic-gate 		if (enc_debug)
10927c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
10937c478bd9Sstevel@tonic-gate 				    "\t(encrypt_is) initial state = %d\n",
10947c478bd9Sstevel@tonic-gate 				    lstate);
10957c478bd9Sstevel@tonic-gate 		/*
10967c478bd9Sstevel@tonic-gate 		 * Before we extract the IV bytes, make sure we got
10977c478bd9Sstevel@tonic-gate 		 * enough data.
10987c478bd9Sstevel@tonic-gate 		 */
10997c478bd9Sstevel@tonic-gate 		if (cnt < sizeof (Block)) {
11007c478bd9Sstevel@tonic-gate 			iv_status = CFB64_IV_BAD;
11017c478bd9Sstevel@tonic-gate 			if (enc_debug)
11027c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
11037c478bd9Sstevel@tonic-gate 					    "\t(encrypt_is) Not enough "
11047c478bd9Sstevel@tonic-gate 					    "IV bytes\n");
11057c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_NOT_READY;
11067c478bd9Sstevel@tonic-gate 		} else {
11077c478bd9Sstevel@tonic-gate 			data++; /* skip over the CFB64_IV byte */
11087c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.decrypt.ivec, data,
11097c478bd9Sstevel@tonic-gate 				    sizeof (Block));
11107c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
11117c478bd9Sstevel@tonic-gate 		}
11127c478bd9Sstevel@tonic-gate 		break;
11137c478bd9Sstevel@tonic-gate 	case TELOPT_ENCTYPE_NULL:
11147c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = type;
11157c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV;
11167c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV;
11177c478bd9Sstevel@tonic-gate 		if (enc_debug)
11187c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
11197c478bd9Sstevel@tonic-gate 				"\t(encrypt_is) We accept NULL encr\n");
11207c478bd9Sstevel@tonic-gate 		break;
11217c478bd9Sstevel@tonic-gate 	default:
11227c478bd9Sstevel@tonic-gate 		iv_status = CFB64_IV_BAD;
11237c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = NULL;
11247c478bd9Sstevel@tonic-gate 		if (enc_debug)
11257c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
11267c478bd9Sstevel@tonic-gate 				    "\t(encrypt_is) Can't find type (%d) "
11277c478bd9Sstevel@tonic-gate 				    "for initial negotiation\r\n",
11287c478bd9Sstevel@tonic-gate 				    type);
11297c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
11307c478bd9Sstevel@tonic-gate 		break;
11317c478bd9Sstevel@tonic-gate 	}
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	sbbuf[5] = (uchar_t)iv_status; /* either CFB64_IV_OK or BAD */
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate 	if (iv_status == CFB64_IV_OK) {
11367c478bd9Sstevel@tonic-gate 		/*
11377c478bd9Sstevel@tonic-gate 		 * send IV to crypto module and indicate it is for
11387c478bd9Sstevel@tonic-gate 		 * decrypt only
11397c478bd9Sstevel@tonic-gate 		 */
11407c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV;  /* we received an OK IV */
11417c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV;  /* we dont send an IV */
11427c478bd9Sstevel@tonic-gate 	} else {
11437c478bd9Sstevel@tonic-gate 		/* tell crypto module to disable crypto on "read" stream */
11447c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
11457c478bd9Sstevel@tonic-gate 	}
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, sizeof (sbbuf));
11487c478bd9Sstevel@tonic-gate 	netflush();
11497c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
11507c478bd9Sstevel@tonic-gate 	if (enc_debug)
11517c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
11527c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_REPLY %s %s\n",
11537c478bd9Sstevel@tonic-gate 			    ENCTYPE_NAME(type),
11547c478bd9Sstevel@tonic-gate 			    (iv_status == CFB64_IV_OK ? "CFB64_IV_OK" :
11557c478bd9Sstevel@tonic-gate 			    "CFB64_IV_BAD"));
11567c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
11577c478bd9Sstevel@tonic-gate 	/* Update the state of the decryption negotiation */
11587c478bd9Sstevel@tonic-gate 	encr_data.decrypt.state = lstate;
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY)
11617c478bd9Sstevel@tonic-gate 		encr_data.decrypt.autoflag = 0;
11627c478bd9Sstevel@tonic-gate 	else {
11637c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_OK && encr_data.decrypt.autoflag)
11647c478bd9Sstevel@tonic-gate 			encrypt_send_request_start();
11657c478bd9Sstevel@tonic-gate 	}
11667c478bd9Sstevel@tonic-gate 	if (enc_debug)
11677c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
11687c478bd9Sstevel@tonic-gate 			    "\t(encrypt_is) final DECRYPT state = %d\n",
11697c478bd9Sstevel@tonic-gate 			    encr_data.decrypt.state);
11707c478bd9Sstevel@tonic-gate }
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate /*
11737c478bd9Sstevel@tonic-gate  * encrypt_send_encrypt_is
11747c478bd9Sstevel@tonic-gate  *
11757c478bd9Sstevel@tonic-gate  * Tell the client what encryption we will use
11767c478bd9Sstevel@tonic-gate  * and what our IV will be.
11777c478bd9Sstevel@tonic-gate  */
11787c478bd9Sstevel@tonic-gate static int
11797c478bd9Sstevel@tonic-gate encrypt_send_encrypt_is()
11807c478bd9Sstevel@tonic-gate {
11817c478bd9Sstevel@tonic-gate 	register int lstate;
11827c478bd9Sstevel@tonic-gate 	krb5_error_code kret;
11837c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[MAXOPTLEN], *p;
11847c478bd9Sstevel@tonic-gate 	int i;
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	lstate = encr_data.encrypt.state;
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == ENCTYPE_NULL) {
11897c478bd9Sstevel@tonic-gate 		/*
11907c478bd9Sstevel@tonic-gate 		 * Haven't received ENCRYPT SUPPORT yet or we couldn't agree
11917c478bd9Sstevel@tonic-gate 		 * on a cipher.
11927c478bd9Sstevel@tonic-gate 		 */
11937c478bd9Sstevel@tonic-gate 		return (lstate);
11947c478bd9Sstevel@tonic-gate 	}
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	/*
11977c478bd9Sstevel@tonic-gate 	 * - Create a random DES key
11987c478bd9Sstevel@tonic-gate 	 *
11997c478bd9Sstevel@tonic-gate 	 * - DES ECB encrypt
12007c478bd9Sstevel@tonic-gate 	 *   encrypt the IV using itself as the key.
12017c478bd9Sstevel@tonic-gate 	 *
12027c478bd9Sstevel@tonic-gate 	 * - Send response
12037c478bd9Sstevel@tonic-gate 	 *   IAC SB TELOPT_ENCRYPT ENCRYPT_IS CFB64 FB64_IV [ feed block ]
12047c478bd9Sstevel@tonic-gate 	 *   IAC SE
12057c478bd9Sstevel@tonic-gate 	 *
12067c478bd9Sstevel@tonic-gate 	 */
12077c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY)
12087c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_IN_PROGRESS;
12097c478bd9Sstevel@tonic-gate 	else if ((lstate & ENCR_STATE_NO_SEND_IV) == 0) {
12107c478bd9Sstevel@tonic-gate 		if (enc_debug)
12117c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
12127c478bd9Sstevel@tonic-gate 				"\t(encrypt_send_is) IV already sent,"
12137c478bd9Sstevel@tonic-gate 				" state = %d\n", lstate);
12147c478bd9Sstevel@tonic-gate 		return (lstate);
12157c478bd9Sstevel@tonic-gate 	}
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 	if (!VALIDKEY(encr_data.encrypt.krbdes_key)) {
12187c478bd9Sstevel@tonic-gate 		/*
12197c478bd9Sstevel@tonic-gate 		 * Invalid key, set flag so we try again later
12207c478bd9Sstevel@tonic-gate 		 * when we get a good one
12217c478bd9Sstevel@tonic-gate 		 */
12227c478bd9Sstevel@tonic-gate 		encr_data.encrypt.need_start = 1;
12237c478bd9Sstevel@tonic-gate 		if (enc_debug)
12247c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
12257c478bd9Sstevel@tonic-gate 				"\t(encrypt_send_is) No Key, cannot "
12267c478bd9Sstevel@tonic-gate 				"start encryption yet\n");
12277c478bd9Sstevel@tonic-gate 		return (lstate);
12287c478bd9Sstevel@tonic-gate 	}
12297c478bd9Sstevel@tonic-gate 	if (enc_debug)
12307c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
12317c478bd9Sstevel@tonic-gate 			    "\t(encrypt_send_is) Creating new feed\n");
12327c478bd9Sstevel@tonic-gate 
12337c478bd9Sstevel@tonic-gate 	/*
12347c478bd9Sstevel@tonic-gate 	 * Create a random feed and send it over.
12357c478bd9Sstevel@tonic-gate 	 *
12367c478bd9Sstevel@tonic-gate 	 * Use the /dev/[u]random interface to generate
12377c478bd9Sstevel@tonic-gate 	 * our encryption IV.
12387c478bd9Sstevel@tonic-gate 	 */
12397c478bd9Sstevel@tonic-gate 	kret = getrandom((char *)encr_data.encrypt.ivec, sizeof (Block));
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	if (kret) {
12427c478bd9Sstevel@tonic-gate 		if (enc_debug)
12437c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
12447c478bd9Sstevel@tonic-gate 				    "\t(encrypt_send_is) error from "
12457c478bd9Sstevel@tonic-gate 				    "getrandom: %d\n", kret);
12467c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Failed to create encryption key (err %d)\n");
12477c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = ENCTYPE_NULL;
12487c478bd9Sstevel@tonic-gate 	} else {
12497c478bd9Sstevel@tonic-gate 		mit_des_fixup_key_parity(encr_data.encrypt.ivec);
12507c478bd9Sstevel@tonic-gate 	}
12517c478bd9Sstevel@tonic-gate 
12527c478bd9Sstevel@tonic-gate 	p = sbbuf;
12537c478bd9Sstevel@tonic-gate 	*p++ = IAC;
12547c478bd9Sstevel@tonic-gate 	*p++ = SB;
12557c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
12567c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_IS;
12577c478bd9Sstevel@tonic-gate 	*p++ = encr_data.encrypt.type;
12587c478bd9Sstevel@tonic-gate 	*p++ = CFB64_IV;
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 	/*
12617c478bd9Sstevel@tonic-gate 	 * Copy the IV bytes individually so that when a
12627c478bd9Sstevel@tonic-gate 	 * 255 (telnet IAC) is used, it can be "escaped" by
12637c478bd9Sstevel@tonic-gate 	 * adding it twice (telnet RFC 854).
12647c478bd9Sstevel@tonic-gate 	 */
12657c478bd9Sstevel@tonic-gate 	for (i = 0; i < sizeof (Block); i++)
12667c478bd9Sstevel@tonic-gate 		if ((*p++ = encr_data.encrypt.ivec[i]) == IAC)
12677c478bd9Sstevel@tonic-gate 			*p++ = IAC;
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	*p++ = IAC;
12707c478bd9Sstevel@tonic-gate 	*p++ = SE;
12717c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
12727c478bd9Sstevel@tonic-gate 	netflush();
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 	if (!kret) {
12757c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* we sent our IV */
12767c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* dont need decrypt IV */
12777c478bd9Sstevel@tonic-gate 	}
12787c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	if (enc_debug) {
12817c478bd9Sstevel@tonic-gate 		int i;
12827c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
12837c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_IS %d CFB64_IV ",
12847c478bd9Sstevel@tonic-gate 			    encr_data.encrypt.type);
12857c478bd9Sstevel@tonic-gate 		for (i = 0; i < (p-sbbuf); i++)
12867c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%d ", (int)sbbuf[i]);
12877c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
12887c478bd9Sstevel@tonic-gate 	}
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	return (lstate);
12917c478bd9Sstevel@tonic-gate }
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate /*
12947c478bd9Sstevel@tonic-gate  * stop_stream
12957c478bd9Sstevel@tonic-gate  *
12967c478bd9Sstevel@tonic-gate  * Utility routine to send a CRIOCSTOP ioctl to the
12977c478bd9Sstevel@tonic-gate  * crypto module (cryptmod).
12987c478bd9Sstevel@tonic-gate  */
12997c478bd9Sstevel@tonic-gate static void
13007c478bd9Sstevel@tonic-gate stop_stream(int fd, int dir)
13017c478bd9Sstevel@tonic-gate {
13027c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
13037c478bd9Sstevel@tonic-gate 	uint32_t stopdir = dir;
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSTOP;
13067c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
13077c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (stopdir);
13087c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&stopdir;
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_STR, &crioc)) {
13117c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
13127c478bd9Sstevel@tonic-gate 	}
13137c478bd9Sstevel@tonic-gate }
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate /*
13167c478bd9Sstevel@tonic-gate  * start_stream
13177c478bd9Sstevel@tonic-gate  *
13187c478bd9Sstevel@tonic-gate  * Utility routine to send a CRYPTIOCSTART ioctl to the
13197c478bd9Sstevel@tonic-gate  * crypto module (cryptmod).  This routine may contain optional
13207c478bd9Sstevel@tonic-gate  * payload data that the cryptmod will interpret as bytes that
13217c478bd9Sstevel@tonic-gate  * need to be decrypted and sent back up to the application
13227c478bd9Sstevel@tonic-gate  * via the data stream.
13237c478bd9Sstevel@tonic-gate  */
13247c478bd9Sstevel@tonic-gate static void
13257c478bd9Sstevel@tonic-gate start_stream(int fd, int dir, int datalen, char *data)
13267c478bd9Sstevel@tonic-gate {
13277c478bd9Sstevel@tonic-gate 	struct strioctl crioc;
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = (dir == CRYPT_ENCRYPT ? CRYPTIOCSTARTENC :
13307c478bd9Sstevel@tonic-gate 			CRYPTIOCSTARTDEC);
13317c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
13327c478bd9Sstevel@tonic-gate 	crioc.ic_len = datalen;
13337c478bd9Sstevel@tonic-gate 	crioc.ic_dp = data;
13347c478bd9Sstevel@tonic-gate 
13357c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_STR, &crioc)) {
13367c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
13377c478bd9Sstevel@tonic-gate 	}
13387c478bd9Sstevel@tonic-gate }
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate /*
13417c478bd9Sstevel@tonic-gate  * encrypt_start_output
13427c478bd9Sstevel@tonic-gate  *
13437c478bd9Sstevel@tonic-gate  * Tell the other side to start encrypting its data
13447c478bd9Sstevel@tonic-gate  */
13457c478bd9Sstevel@tonic-gate static void
13467c478bd9Sstevel@tonic-gate encrypt_start_output()
13477c478bd9Sstevel@tonic-gate {
13487c478bd9Sstevel@tonic-gate 	int lstate;
13497c478bd9Sstevel@tonic-gate 	uchar_t *p;
13507c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[MAXOPTLEN];
13517c478bd9Sstevel@tonic-gate 	struct strioctl crioc;
13527c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	/*
13557c478bd9Sstevel@tonic-gate 	 * Initialize crypto and send the ENCRYPT_IS msg
13567c478bd9Sstevel@tonic-gate 	 */
13577c478bd9Sstevel@tonic-gate 	lstate = encrypt_send_encrypt_is();
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	if (lstate != ENCR_STATE_OK) {
13607c478bd9Sstevel@tonic-gate 		if (enc_debug)
13617c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
13627c478bd9Sstevel@tonic-gate 				"\t(encrypt_start_output) ENCRYPT state "
13637c478bd9Sstevel@tonic-gate 				"= %d\n", lstate);
13647c478bd9Sstevel@tonic-gate 		return;
13657c478bd9Sstevel@tonic-gate 	}
13667c478bd9Sstevel@tonic-gate 
13677c478bd9Sstevel@tonic-gate 	p = sbbuf;
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	*p++ = IAC;
13707c478bd9Sstevel@tonic-gate 	*p++ = SB;
13717c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
13727c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_START;
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	(void) memcpy(p, encr_data.encrypt.keyid, encr_data.encrypt.keyidlen);
13757c478bd9Sstevel@tonic-gate 	p += encr_data.encrypt.keyidlen;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 	*p++ = IAC;
13787c478bd9Sstevel@tonic-gate 	*p++ = SE;
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	/* Flush this data out before we start encrypting */
13817c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (int)(p-sbbuf));
13827c478bd9Sstevel@tonic-gate 	netflush();
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	if (enc_debug)
13857c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_START %d "
13867c478bd9Sstevel@tonic-gate 			"(lstate = %d) data waiting = %d\n",
13877c478bd9Sstevel@tonic-gate 			(int)encr_data.encrypt.keyid[0],
13887c478bd9Sstevel@tonic-gate 			lstate, nfrontp-nbackp);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	/*
13937c478bd9Sstevel@tonic-gate 	 * tell crypto module what key to use for encrypting
13947c478bd9Sstevel@tonic-gate 	 * Note that the ENCRYPT has not yet been enabled, but we
13957c478bd9Sstevel@tonic-gate 	 * need to first set the crypto key to use.
13967c478bd9Sstevel@tonic-gate 	 */
13977c478bd9Sstevel@tonic-gate 	cki.direction_mask = CRYPT_ENCRYPT;
13987c478bd9Sstevel@tonic-gate 
13997c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
14007c478bd9Sstevel@tonic-gate 		cki.crypto_method = CRYPT_METHOD_DES_CFB;
14017c478bd9Sstevel@tonic-gate 	} else {
14027c478bd9Sstevel@tonic-gate 		if (enc_debug)
14037c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
14047c478bd9Sstevel@tonic-gate 				"\t(encrypt_start_output) - unknown "
14057c478bd9Sstevel@tonic-gate 				"crypto_method %d\n",
14067c478bd9Sstevel@tonic-gate 				encr_data.encrypt.type);
14077c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unrecognized crypto encrypt method: %d",
14087c478bd9Sstevel@tonic-gate 				encr_data.encrypt.type);
14097c478bd9Sstevel@tonic-gate 
14107c478bd9Sstevel@tonic-gate 		return;
14117c478bd9Sstevel@tonic-gate 	}
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	/*
14147c478bd9Sstevel@tonic-gate 	 * If we previously configured this crypto method, we dont want to
14157c478bd9Sstevel@tonic-gate 	 * overwrite the key or ivec information already given to the crypto
14167c478bd9Sstevel@tonic-gate 	 * module as it will cause the cipher data between the client and server
14177c478bd9Sstevel@tonic-gate 	 * to become out of synch and impossible to decipher.
14187c478bd9Sstevel@tonic-gate 	 */
14197c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.setup == cki.crypto_method) {
14207c478bd9Sstevel@tonic-gate 		cki.keylen = 0;
14217c478bd9Sstevel@tonic-gate 		cki.iveclen = 0;
14227c478bd9Sstevel@tonic-gate 	} else {
14237c478bd9Sstevel@tonic-gate 		cki.keylen = DES_BLOCKSIZE;
14247c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.key, (void *)encr_data.encrypt.krbdes_key,
14257c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 		cki.iveclen = DES_BLOCKSIZE;
14287c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.ivec, (void *)encr_data.encrypt.ivec,
14297c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 		cki.ivec_usage = IVEC_ONETIME;
14327c478bd9Sstevel@tonic-gate 	}
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	/* Stop encrypt side prior to setup so we dont lose data */
14377c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, CRYPT_ENCRYPT);
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
14407c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
14417c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (struct cr_info_t);
14427c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
14457c478bd9Sstevel@tonic-gate 		perror("ioctl(CRYPTIOCSETUP) [encrypt_start_output] error");
14467c478bd9Sstevel@tonic-gate 	} else {
14477c478bd9Sstevel@tonic-gate 		/* Setup completed OK */
14487c478bd9Sstevel@tonic-gate 		encr_data.encrypt.setup = cki.crypto_method;
14497c478bd9Sstevel@tonic-gate 	}
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 	/*
14527c478bd9Sstevel@tonic-gate 	 * We do not check for "stuck" data when setting up the
14537c478bd9Sstevel@tonic-gate 	 * outbound "encrypt" channel.  Any data queued prior to
14547c478bd9Sstevel@tonic-gate 	 * this IOCTL will get processed correctly without our help.
14557c478bd9Sstevel@tonic-gate 	 */
14567c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, CRYPT_ENCRYPT, 0, NULL);
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	/*
14597c478bd9Sstevel@tonic-gate 	 * tell crypto module to start encrypting
14607c478bd9Sstevel@tonic-gate 	 */
14617c478bd9Sstevel@tonic-gate 	if (enc_debug)
14627c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
14637c478bd9Sstevel@tonic-gate 			"\t(encrypt_start_output) Encrypting output\n");
14647c478bd9Sstevel@tonic-gate }
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate /*
14677c478bd9Sstevel@tonic-gate  * encrypt_request_start
14687c478bd9Sstevel@tonic-gate  *
14697c478bd9Sstevel@tonic-gate  * The client requests that we start encryption immediately after
14707c478bd9Sstevel@tonic-gate  * successful negotiation
14717c478bd9Sstevel@tonic-gate  */
14727c478bd9Sstevel@tonic-gate static void
14737c478bd9Sstevel@tonic-gate encrypt_request_start(void)
14747c478bd9Sstevel@tonic-gate {
14757c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == ENCTYPE_NULL) {
14767c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 1;
14777c478bd9Sstevel@tonic-gate 		if (enc_debug)
14787c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\t(encrypt_request_start) "
14797c478bd9Sstevel@tonic-gate 				"autoencrypt = ON\n");
14807c478bd9Sstevel@tonic-gate 	} else {
14817c478bd9Sstevel@tonic-gate 		encrypt_start_output();
14827c478bd9Sstevel@tonic-gate 	}
14837c478bd9Sstevel@tonic-gate }
14847c478bd9Sstevel@tonic-gate 
14857c478bd9Sstevel@tonic-gate /*
14867c478bd9Sstevel@tonic-gate  * encrypt_end
14877c478bd9Sstevel@tonic-gate  *
14887c478bd9Sstevel@tonic-gate  * ENCRYPT END received, stop decrypting the read stream
14897c478bd9Sstevel@tonic-gate  */
14907c478bd9Sstevel@tonic-gate static void
14917c478bd9Sstevel@tonic-gate encrypt_end(int direction)
14927c478bd9Sstevel@tonic-gate {
14937c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
14947c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
14957c478bd9Sstevel@tonic-gate 	uint32_t stopdir;
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate 	stopdir = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
14987c478bd9Sstevel@tonic-gate 		CRYPT_ENCRYPT);
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, stopdir);
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 	/*
15037c478bd9Sstevel@tonic-gate 	 * Call this function when we wish to disable crypto in
15047c478bd9Sstevel@tonic-gate 	 * either direction (ENCRYPT or DECRYPT)
15057c478bd9Sstevel@tonic-gate 	 */
15067c478bd9Sstevel@tonic-gate 	cki.direction_mask = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
15077c478bd9Sstevel@tonic-gate 			    CRYPT_ENCRYPT);
15087c478bd9Sstevel@tonic-gate 	cki.crypto_method = CRYPT_METHOD_NONE;
15097c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 	cki.keylen = 0;
15127c478bd9Sstevel@tonic-gate 	cki.iveclen = 0;
15133ff1fd9dSethindra 	cki.ivec_usage = IVEC_ONETIME;
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
15167c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
15177c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (cki);
15187c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
15217c478bd9Sstevel@tonic-gate 		perror("ioctl(CRYPTIOCSETUP) [encrypt_end] error");
15227c478bd9Sstevel@tonic-gate 	}
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, stopdir, 0, NULL);
15257c478bd9Sstevel@tonic-gate }
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate /*
15287c478bd9Sstevel@tonic-gate  * encrypt_request_end
15297c478bd9Sstevel@tonic-gate  *
15307c478bd9Sstevel@tonic-gate  * When we receive a REQEND from the client, it means
15317c478bd9Sstevel@tonic-gate  * that we are supposed to stop encrypting
15327c478bd9Sstevel@tonic-gate  */
15337c478bd9Sstevel@tonic-gate static void
15347c478bd9Sstevel@tonic-gate encrypt_request_end()
15357c478bd9Sstevel@tonic-gate {
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * Tell the other side we are done encrypting
15387c478bd9Sstevel@tonic-gate 	 */
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 	write_data("%c%c%c%c%c%c",
15417c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
15427c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
15437c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
15447c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_END,
15457c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
15467c478bd9Sstevel@tonic-gate 		(uchar_t)SE);
15477c478bd9Sstevel@tonic-gate 	netflush();
15487c478bd9Sstevel@tonic-gate 	if (enc_debug)
15497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_END\n");
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 	/*
15527c478bd9Sstevel@tonic-gate 	 * Turn off encryption of the write stream
15537c478bd9Sstevel@tonic-gate 	 */
15547c478bd9Sstevel@tonic-gate 	encrypt_end(TELNET_DIR_ENCRYPT);
15557c478bd9Sstevel@tonic-gate }
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate /*
15587c478bd9Sstevel@tonic-gate  * encrypt_send_request_end
15597c478bd9Sstevel@tonic-gate  *
15607c478bd9Sstevel@tonic-gate  * We stop encrypting the write stream and tell the other side about it.
15617c478bd9Sstevel@tonic-gate  */
15627c478bd9Sstevel@tonic-gate static void
15637c478bd9Sstevel@tonic-gate encrypt_send_request_end()
15647c478bd9Sstevel@tonic-gate {
15657c478bd9Sstevel@tonic-gate 	write_data("%c%c%c%c%c%c",
15667c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
15677c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
15687c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
15697c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_REQEND,
15707c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
15717c478bd9Sstevel@tonic-gate 		(uchar_t)SE);
15727c478bd9Sstevel@tonic-gate 	netflush();
15737c478bd9Sstevel@tonic-gate 	if (enc_debug)
15747c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_REQEND\n");
15757c478bd9Sstevel@tonic-gate }
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate /*
15787c478bd9Sstevel@tonic-gate  * encrypt_start
15797c478bd9Sstevel@tonic-gate  *
15807c478bd9Sstevel@tonic-gate  * The client is going to start sending encrypted data
15817c478bd9Sstevel@tonic-gate  * using the previously negotiated cipher (see what we set
15827c478bd9Sstevel@tonic-gate  * when we did the REPLY in encrypt_is).
15837c478bd9Sstevel@tonic-gate  */
15847c478bd9Sstevel@tonic-gate static void
15857c478bd9Sstevel@tonic-gate encrypt_start(void)
15867c478bd9Sstevel@tonic-gate {
15877c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
15887c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
15897c478bd9Sstevel@tonic-gate 	int bytes = 0;
15907c478bd9Sstevel@tonic-gate 	char *dataptr = NULL;
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.type == ENCTYPE_NULL) {
15937c478bd9Sstevel@tonic-gate 		if (enc_debug)
15947c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
15957c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) No DECRYPT method "
15967c478bd9Sstevel@tonic-gate 				"defined yet\n");
15977c478bd9Sstevel@tonic-gate 		encrypt_send_request_end();
15987c478bd9Sstevel@tonic-gate 		return;
15997c478bd9Sstevel@tonic-gate 	}
16007c478bd9Sstevel@tonic-gate 
16017c478bd9Sstevel@tonic-gate 	cki.direction_mask = CRYPT_DECRYPT;
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
16047c478bd9Sstevel@tonic-gate 		cki.crypto_method = CRYPT_METHOD_DES_CFB;
16057c478bd9Sstevel@tonic-gate 	} else {
16067c478bd9Sstevel@tonic-gate 		if (enc_debug)
16077c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
16087c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) - unknown "
16097c478bd9Sstevel@tonic-gate 				"crypto_method %d\n", encr_data.decrypt.type);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unrecognized crypto decrypt method: %d",
16127c478bd9Sstevel@tonic-gate 				encr_data.decrypt.type);
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 		return;
16157c478bd9Sstevel@tonic-gate 	}
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	/*
16187c478bd9Sstevel@tonic-gate 	 * Don't overwrite previously configured key and ivec info
16197c478bd9Sstevel@tonic-gate 	 */
16207c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.setup != cki.crypto_method) {
16217c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.key, (void *)encr_data.decrypt.krbdes_key,
16227c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
16237c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.ivec, (void *)encr_data.decrypt.ivec,
16247c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 		cki.keylen = DES_BLOCKSIZE;
16277c478bd9Sstevel@tonic-gate 		cki.iveclen = DES_BLOCKSIZE;
16287c478bd9Sstevel@tonic-gate 		cki.ivec_usage = IVEC_ONETIME;
16297c478bd9Sstevel@tonic-gate 	} else {
16307c478bd9Sstevel@tonic-gate 		cki.keylen = 0;
16317c478bd9Sstevel@tonic-gate 		cki.iveclen = 0;
16327c478bd9Sstevel@tonic-gate 	}
16337c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, CRYPT_DECRYPT);
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
16387c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
16397c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (struct cr_info_t);
16407c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
16417c478bd9Sstevel@tonic-gate 
16427c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
16437c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "ioctl(CRYPTIOCSETUP) [encrypt_start] "
16447c478bd9Sstevel@tonic-gate 		    "error: %m");
16457c478bd9Sstevel@tonic-gate 	} else {
16467c478bd9Sstevel@tonic-gate 		encr_data.decrypt.setup = cki.crypto_method;
16477c478bd9Sstevel@tonic-gate 	}
16487c478bd9Sstevel@tonic-gate 	if (enc_debug)
16497c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
16507c478bd9Sstevel@tonic-gate 			    "\t(encrypt_start) called CRYPTIOCSETUP for "
16517c478bd9Sstevel@tonic-gate 			    "decrypt side\n");
16527c478bd9Sstevel@tonic-gate 
16537c478bd9Sstevel@tonic-gate 	/*
16547c478bd9Sstevel@tonic-gate 	 * Read any data stuck between the cryptmod and the application
16557c478bd9Sstevel@tonic-gate 	 * so we can pass it back down to be properly decrypted after
16567c478bd9Sstevel@tonic-gate 	 * this operation finishes.
16577c478bd9Sstevel@tonic-gate 	 */
16587c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_NREAD, &bytes) < 0) {
16597c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "I_NREAD returned error %m");
16607c478bd9Sstevel@tonic-gate 		bytes = 0;
16617c478bd9Sstevel@tonic-gate 	}
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate 	/*
16647c478bd9Sstevel@tonic-gate 	 * Any data which was read AFTER the ENCRYPT START message
16657c478bd9Sstevel@tonic-gate 	 * must be sent back down to be decrypted properly.
16667c478bd9Sstevel@tonic-gate 	 *
16677c478bd9Sstevel@tonic-gate 	 * 'ncc' is the number of bytes that have been read but
16687c478bd9Sstevel@tonic-gate 	 * not yet processed by the telnet state machine.
16697c478bd9Sstevel@tonic-gate 	 *
16707c478bd9Sstevel@tonic-gate 	 * 'bytes' is the number of bytes waiting to be read from
16717c478bd9Sstevel@tonic-gate 	 * the stream.
16727c478bd9Sstevel@tonic-gate 	 *
16737c478bd9Sstevel@tonic-gate 	 * If either one is a positive value, then those bytes
16747c478bd9Sstevel@tonic-gate 	 * must be pulled up and sent back down to be decrypted.
16757c478bd9Sstevel@tonic-gate 	 */
16767c478bd9Sstevel@tonic-gate 	if (ncc || bytes) {
16777c478bd9Sstevel@tonic-gate 		drainstream(bytes);
16787c478bd9Sstevel@tonic-gate 		if (enc_debug)
16797c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
16807c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) after drainstream, "
16817c478bd9Sstevel@tonic-gate 				"ncc=%d bytes = %d\n", ncc, bytes);
16827c478bd9Sstevel@tonic-gate 		bytes += ncc;
16837c478bd9Sstevel@tonic-gate 		dataptr = netip;
16847c478bd9Sstevel@tonic-gate 	}
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, CRYPT_DECRYPT, bytes, dataptr);
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	/*
16897c478bd9Sstevel@tonic-gate 	 * The bytes putback into the stream are no longer
16907c478bd9Sstevel@tonic-gate 	 * available to be read by the server, so adjust the
16917c478bd9Sstevel@tonic-gate 	 * counter accordingly.
16927c478bd9Sstevel@tonic-gate 	 */
16937c478bd9Sstevel@tonic-gate 	ncc = 0;
16947c478bd9Sstevel@tonic-gate 	netip = netibuf;
16957c478bd9Sstevel@tonic-gate 	(void) memset(netip, 0, netibufsize);
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
16987c478bd9Sstevel@tonic-gate 	if (enc_debug) {
16997c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
17007c478bd9Sstevel@tonic-gate 			    "\t(encrypt_start) Start DECRYPT using %s\n",
17017c478bd9Sstevel@tonic-gate 			    ENCTYPE_NAME(encr_data.decrypt.type));
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
17047c478bd9Sstevel@tonic-gate }
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate /*
17077c478bd9Sstevel@tonic-gate  * encrypt_support
17087c478bd9Sstevel@tonic-gate  *
17097c478bd9Sstevel@tonic-gate  * Called when we recieve the TELOPT_ENCRYPT SUPPORT [ encr type list ]
17107c478bd9Sstevel@tonic-gate  * message from a client.
17117c478bd9Sstevel@tonic-gate  *
17127c478bd9Sstevel@tonic-gate  * Choose an agreeable method (DES_CFB64) and
17137c478bd9Sstevel@tonic-gate  * respond with  TELOPT_ENCRYPT ENCRYPT_IS [ desired crypto method ]
17147c478bd9Sstevel@tonic-gate  *
17157c478bd9Sstevel@tonic-gate  * from: RFC 2946
17167c478bd9Sstevel@tonic-gate  */
17177c478bd9Sstevel@tonic-gate static void
17187c478bd9Sstevel@tonic-gate encrypt_support(char *data, int cnt)
17197c478bd9Sstevel@tonic-gate {
17207c478bd9Sstevel@tonic-gate 	int lstate = ENCR_STATE_NOT_READY;
17217c478bd9Sstevel@tonic-gate 	int type, use_type = 0;
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	while (cnt-- > 0 && use_type == 0) {
17247c478bd9Sstevel@tonic-gate 		type = *data++;
17257c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
17267c478bd9Sstevel@tonic-gate 		if (enc_debug)
17277c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
17287c478bd9Sstevel@tonic-gate 				    "RCVD ENCRYPT SUPPORT %s\n",
17297c478bd9Sstevel@tonic-gate 				    ENCTYPE_NAME(type));
17307c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
17317c478bd9Sstevel@tonic-gate 		/*
17327c478bd9Sstevel@tonic-gate 		 * Prefer CFB64
17337c478bd9Sstevel@tonic-gate 		 */
17347c478bd9Sstevel@tonic-gate 		if (type == TELOPT_ENCTYPE_DES_CFB64) {
17357c478bd9Sstevel@tonic-gate 			use_type = type;
17367c478bd9Sstevel@tonic-gate 		}
17377c478bd9Sstevel@tonic-gate 	}
17387c478bd9Sstevel@tonic-gate 	encr_data.encrypt.type = use_type;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 	if (use_type != TELOPT_ENCTYPE_NULL &&
17417c478bd9Sstevel@tonic-gate 	    authenticated != NULL && authenticated != &NoAuth &&
17427c478bd9Sstevel@tonic-gate 	    auth_status != AUTH_REJECT) {
17437c478bd9Sstevel@tonic-gate 
17447c478bd9Sstevel@tonic-gate 		/* Authenticated -> have session key -> send ENCRYPT IS */
17457c478bd9Sstevel@tonic-gate 		lstate = encrypt_send_encrypt_is();
17467c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_OK)
17477c478bd9Sstevel@tonic-gate 			encrypt_start_output();
17487c478bd9Sstevel@tonic-gate 	} else if (use_type == TELOPT_ENCTYPE_NULL) {
17497c478bd9Sstevel@tonic-gate 		if (enc_debug)
17507c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
17517c478bd9Sstevel@tonic-gate 				    "\t(encrypt_support) Cannot agree "
17527c478bd9Sstevel@tonic-gate 				    "on crypto algorithm, output encryption "
17537c478bd9Sstevel@tonic-gate 				    "disabled.\n");
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 		/*
17567c478bd9Sstevel@tonic-gate 		 * Cannot agree on crypto algorithm
17577c478bd9Sstevel@tonic-gate 		 * RFC 2946 sez:
17587c478bd9Sstevel@tonic-gate 		 *    send "IAC SB ENCRYPT IS NULL IAC SE"
17597c478bd9Sstevel@tonic-gate 		 *    optionally, also send IAC WONT ENCRYPT
17607c478bd9Sstevel@tonic-gate 		 */
17617c478bd9Sstevel@tonic-gate 		write_data("%c%c%c%c%c%c%c",
17627c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
17637c478bd9Sstevel@tonic-gate 			(uchar_t)SB,
17647c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCRYPT,
17657c478bd9Sstevel@tonic-gate 			(uchar_t)ENCRYPT_IS,
17667c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCTYPE_NULL,
17677c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
17687c478bd9Sstevel@tonic-gate 			(uchar_t)SE);
17697c478bd9Sstevel@tonic-gate 		send_wont(TELOPT_ENCRYPT);
17707c478bd9Sstevel@tonic-gate 		netflush();
17717c478bd9Sstevel@tonic-gate 		if (enc_debug)
17727c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
17737c478bd9Sstevel@tonic-gate 				    "SENT TELOPT_ENCRYPT ENCRYPT_IS "
17747c478bd9Sstevel@tonic-gate 				    "[NULL]\n");
17757c478bd9Sstevel@tonic-gate 
17767c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] = OPT_NO;
17777c478bd9Sstevel@tonic-gate 	}
17787c478bd9Sstevel@tonic-gate 	settimer(encr_support);
17797c478bd9Sstevel@tonic-gate }
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate /*
17827c478bd9Sstevel@tonic-gate  * encrypt_send_keyid
17837c478bd9Sstevel@tonic-gate  *
17847c478bd9Sstevel@tonic-gate  * Sent the key id we will use to the client
17857c478bd9Sstevel@tonic-gate  */
17867c478bd9Sstevel@tonic-gate static void
17877c478bd9Sstevel@tonic-gate encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, boolean_t saveit)
17887c478bd9Sstevel@tonic-gate {
17897c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[128], *p;
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	p = sbbuf;
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 	*p++ = IAC;
17947c478bd9Sstevel@tonic-gate 	*p++ = SB;
17957c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
17967c478bd9Sstevel@tonic-gate 	*p++ = (dir == TELNET_DIR_ENCRYPT ? ENCRYPT_ENC_KEYID :
17977c478bd9Sstevel@tonic-gate 		ENCRYPT_DEC_KEYID);
17987c478bd9Sstevel@tonic-gate 	if (saveit) {
17997c478bd9Sstevel@tonic-gate 		if (enc_debug)
18007c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
18017c478bd9Sstevel@tonic-gate 				"\t(send_keyid) store %d byte %s keyid\n",
18027c478bd9Sstevel@tonic-gate 				keylen,
18037c478bd9Sstevel@tonic-gate 				(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
18047c478bd9Sstevel@tonic-gate 				"DECRYPT"));
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 		if (dir == TELNET_DIR_ENCRYPT) {
18077c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.encrypt.keyid, keyid, keylen);
18087c478bd9Sstevel@tonic-gate 			encr_data.encrypt.keyidlen = keylen;
18097c478bd9Sstevel@tonic-gate 		} else {
18107c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.decrypt.keyid, keyid, keylen);
18117c478bd9Sstevel@tonic-gate 			encr_data.decrypt.keyidlen = keylen;
18127c478bd9Sstevel@tonic-gate 		}
18137c478bd9Sstevel@tonic-gate 	}
18147c478bd9Sstevel@tonic-gate 	(void) memcpy(p, keyid, keylen);
18157c478bd9Sstevel@tonic-gate 	p += keylen;
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	*p++ = IAC;
18187c478bd9Sstevel@tonic-gate 	*p++ = SE;
18197c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
18207c478bd9Sstevel@tonic-gate 	netflush();
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 	if (enc_debug)
18237c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT %s %d\n",
18247c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? "ENC_KEYID" :
18257c478bd9Sstevel@tonic-gate 			"DEC_KEYID"), keyid[0]);
18267c478bd9Sstevel@tonic-gate }
18277c478bd9Sstevel@tonic-gate 
18287c478bd9Sstevel@tonic-gate /*
18297c478bd9Sstevel@tonic-gate  * encrypt_reply
18307c478bd9Sstevel@tonic-gate  *
18317c478bd9Sstevel@tonic-gate  * When we receive the TELOPT_ENCRYPT REPLY [crtype] CFB64_IV_OK IAC SE
18327c478bd9Sstevel@tonic-gate  * message, process it accordingly.
18337c478bd9Sstevel@tonic-gate  * If the vector is acceptable, tell client we are encrypting and
18347c478bd9Sstevel@tonic-gate  * enable encryption on our write stream.
18357c478bd9Sstevel@tonic-gate  *
18367c478bd9Sstevel@tonic-gate  * Negotiate the KEYID next..
18377c478bd9Sstevel@tonic-gate  * RFC 2946, 2952
18387c478bd9Sstevel@tonic-gate  */
18397c478bd9Sstevel@tonic-gate static void
18407c478bd9Sstevel@tonic-gate encrypt_reply(char *data, int len)
18417c478bd9Sstevel@tonic-gate {
18427c478bd9Sstevel@tonic-gate 	uchar_t type = (uchar_t)(*data++);
18437c478bd9Sstevel@tonic-gate 	uchar_t result = (uchar_t)(*data);
18447c478bd9Sstevel@tonic-gate 	int lstate;
18457c478bd9Sstevel@tonic-gate 
18467c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
18477c478bd9Sstevel@tonic-gate 	if (enc_debug)
18487c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
18497c478bd9Sstevel@tonic-gate 			"\t(encrypt_reply) ENCRYPT REPLY %s %s [len=%d]\n",
18507c478bd9Sstevel@tonic-gate 			ENCRYPT_NAME(type),
18517c478bd9Sstevel@tonic-gate 			(result == CFB64_IV_OK ? "CFB64_IV_OK" :
18527c478bd9Sstevel@tonic-gate 			"CFB64_IV_BAD"), len);
18537c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate 	lstate = encr_data.encrypt.state;
18567c478bd9Sstevel@tonic-gate 	if (enc_debug)
18577c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
18587c478bd9Sstevel@tonic-gate 			"\t(encrypt_reply) initial ENCRYPT state = %d\n",
18597c478bd9Sstevel@tonic-gate 			lstate);
18607c478bd9Sstevel@tonic-gate 	switch (result) {
18617c478bd9Sstevel@tonic-gate 	case CFB64_IV_OK:
18627c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_NOT_READY)
18637c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
18647c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV; /* we got the IV */
18657c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont need to send IV */
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate 		/*
18687c478bd9Sstevel@tonic-gate 		 * The correct response here is to send the encryption key id
18697c478bd9Sstevel@tonic-gate 		 * RFC 2752.
18707c478bd9Sstevel@tonic-gate 		 *
18717c478bd9Sstevel@tonic-gate 		 * Send keyid 0 to indicate that we will just use default
18727c478bd9Sstevel@tonic-gate 		 * keys.
18737c478bd9Sstevel@tonic-gate 		 */
18747c478bd9Sstevel@tonic-gate 		encrypt_send_keyid(TELNET_DIR_ENCRYPT, (uchar_t *)"\0", 1, 1);
18757c478bd9Sstevel@tonic-gate 
18767c478bd9Sstevel@tonic-gate 		break;
18777c478bd9Sstevel@tonic-gate 	case CFB64_IV_BAD:
18787c478bd9Sstevel@tonic-gate 		/*
18797c478bd9Sstevel@tonic-gate 		 * Clear the ivec
18807c478bd9Sstevel@tonic-gate 		 */
18817c478bd9Sstevel@tonic-gate 		(void) memset(encr_data.encrypt.ivec, 0, sizeof (Block));
18827c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
18837c478bd9Sstevel@tonic-gate 		break;
18847c478bd9Sstevel@tonic-gate 	default:
18857c478bd9Sstevel@tonic-gate 		if (enc_debug)
18867c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
18877c478bd9Sstevel@tonic-gate 				"\t(encrypt_reply) Got unknown IV value in "
18887c478bd9Sstevel@tonic-gate 				"REPLY message\n");
18897c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
18907c478bd9Sstevel@tonic-gate 		break;
18917c478bd9Sstevel@tonic-gate 	}
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
18947c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY) {
18957c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 0;
18967c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = ENCTYPE_NULL;
18977c478bd9Sstevel@tonic-gate 		if (enc_debug)
18987c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
18997c478bd9Sstevel@tonic-gate 				    "\t(encrypt_reply) encrypt.autoflag = "
19007c478bd9Sstevel@tonic-gate 				    "OFF\n");
19017c478bd9Sstevel@tonic-gate 	} else {
19027c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = type;
19037c478bd9Sstevel@tonic-gate 		if ((lstate == ENCR_STATE_OK) && encr_data.encrypt.autoflag)
19047c478bd9Sstevel@tonic-gate 			encrypt_start_output();
19057c478bd9Sstevel@tonic-gate 	}
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	if (enc_debug)
19087c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
19097c478bd9Sstevel@tonic-gate 			    "\t(encrypt_reply) ENCRYPT final state = %d\n",
19107c478bd9Sstevel@tonic-gate 			    lstate);
19117c478bd9Sstevel@tonic-gate }
19127c478bd9Sstevel@tonic-gate 
19137c478bd9Sstevel@tonic-gate static void
19147c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(uchar_t *keyid, int *keyidlen, int dir)
19157c478bd9Sstevel@tonic-gate {
19167c478bd9Sstevel@tonic-gate 	int lstate;
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate 	lstate = (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state :
19197c478bd9Sstevel@tonic-gate 		encr_data.decrypt.state);
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	if (enc_debug)
19227c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
19237c478bd9Sstevel@tonic-gate 			    "\t(set_keyid_state) %s initial state = %d\n",
19247c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19257c478bd9Sstevel@tonic-gate 			    "DECRYPT"), lstate);
19267c478bd9Sstevel@tonic-gate 
19277c478bd9Sstevel@tonic-gate 	/*
19287c478bd9Sstevel@tonic-gate 	 * Currently, we only support using the default keyid,
19297c478bd9Sstevel@tonic-gate 	 * so it should be an error if the len > 1 or the keyid != 0.
19307c478bd9Sstevel@tonic-gate 	 */
19317c478bd9Sstevel@tonic-gate 	if (*keyidlen != 1 || (*keyid != '\0')) {
19327c478bd9Sstevel@tonic-gate 		if (enc_debug)
19337c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
19347c478bd9Sstevel@tonic-gate 				    "\t(set_keyid_state) unexpected keyid: "
19357c478bd9Sstevel@tonic-gate 				    "len=%d value=%d\n", *keyidlen, *keyid);
19367c478bd9Sstevel@tonic-gate 		*keyidlen = 0;
19377c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "rcvd unexpected keyid %d  - only keyid of 0 "
19387c478bd9Sstevel@tonic-gate 		    "is supported",  *keyid);
19397c478bd9Sstevel@tonic-gate 	} else {
19407c478bd9Sstevel@tonic-gate 		/*
19417c478bd9Sstevel@tonic-gate 		 * We move to the "IN_PROGRESS" state.
19427c478bd9Sstevel@tonic-gate 		 */
19437c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_NOT_READY)
19447c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
19457c478bd9Sstevel@tonic-gate 		/*
19467c478bd9Sstevel@tonic-gate 		 * Clear the NO_KEYID bit because we now have a valid keyid
19477c478bd9Sstevel@tonic-gate 		 */
19487c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_KEYID;
19497c478bd9Sstevel@tonic-gate 	}
19507c478bd9Sstevel@tonic-gate 
19517c478bd9Sstevel@tonic-gate 	if (enc_debug)
19527c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
19537c478bd9Sstevel@tonic-gate 			    "\t(set_keyid_state) %s final state = %d\n",
19547c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19557c478bd9Sstevel@tonic-gate 			    "DECRYPT"), lstate);
19567c478bd9Sstevel@tonic-gate 
19577c478bd9Sstevel@tonic-gate 	if (dir == TELNET_DIR_ENCRYPT)
19587c478bd9Sstevel@tonic-gate 		encr_data.encrypt.state = lstate;
19597c478bd9Sstevel@tonic-gate 	else
19607c478bd9Sstevel@tonic-gate 		encr_data.decrypt.state = lstate;
19617c478bd9Sstevel@tonic-gate }
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate /*
19647c478bd9Sstevel@tonic-gate  * encrypt_keyid
19657c478bd9Sstevel@tonic-gate  *
19667c478bd9Sstevel@tonic-gate  * Set the keyid value in the key_info structure.
19677c478bd9Sstevel@tonic-gate  * if necessary send a response to the sender
19687c478bd9Sstevel@tonic-gate  */
19697c478bd9Sstevel@tonic-gate static void
19707c478bd9Sstevel@tonic-gate encrypt_keyid(uchar_t *newkeyid, int *keyidlen, uchar_t *keyid,
19717c478bd9Sstevel@tonic-gate 	int len, int dir)
19727c478bd9Sstevel@tonic-gate {
19737c478bd9Sstevel@tonic-gate 	if (len > TELNET_MAXNUMKEYS) {
19747c478bd9Sstevel@tonic-gate 		if (enc_debug)
19757c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
19767c478bd9Sstevel@tonic-gate 				    "\t(keyid) keylen too big (%d)\n", len);
19777c478bd9Sstevel@tonic-gate 		return;
19787c478bd9Sstevel@tonic-gate 	}
19797c478bd9Sstevel@tonic-gate 
19807c478bd9Sstevel@tonic-gate 	if (enc_debug) {
19817c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(keyid) set KEYID for %s len = %d\n",
19827c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19837c478bd9Sstevel@tonic-gate 			    "DECRYPT"), len);
19847c478bd9Sstevel@tonic-gate 	}
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate 	if (len == 0) {
19877c478bd9Sstevel@tonic-gate 		if (*keyidlen == 0) {
19887c478bd9Sstevel@tonic-gate 			if (enc_debug)
19897c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
19907c478bd9Sstevel@tonic-gate 					    "\t(keyid) Got 0 length keyid - "
19917c478bd9Sstevel@tonic-gate 					    "failure\n");
19927c478bd9Sstevel@tonic-gate 			return;
19937c478bd9Sstevel@tonic-gate 		}
19947c478bd9Sstevel@tonic-gate 		*keyidlen = 0;
19957c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
19967c478bd9Sstevel@tonic-gate 
19977c478bd9Sstevel@tonic-gate 	} else if (len != *keyidlen || memcmp(keyid, newkeyid, len)) {
19987c478bd9Sstevel@tonic-gate 		if (enc_debug)
19997c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
20007c478bd9Sstevel@tonic-gate 				    "\t(keyid) Setting new key (%d bytes)\n",
20017c478bd9Sstevel@tonic-gate 				    len);
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 		*keyidlen = len;
20047c478bd9Sstevel@tonic-gate 		(void) memcpy(newkeyid, keyid, len);
20057c478bd9Sstevel@tonic-gate 
20067c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
20077c478bd9Sstevel@tonic-gate 	} else {
20087c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 		if (enc_debug)
20117c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
20127c478bd9Sstevel@tonic-gate 				    "\t(keyid) %s Key already in place,"
20137c478bd9Sstevel@tonic-gate 				    "state = %d autoflag=%d\n",
20147c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" : "DECRYPT"),
20157c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state:
20167c478bd9Sstevel@tonic-gate 			encr_data.decrypt.state),
20177c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ?
20187c478bd9Sstevel@tonic-gate 				encr_data.encrypt.autoflag:
20197c478bd9Sstevel@tonic-gate 				encr_data.decrypt.autoflag));
20207c478bd9Sstevel@tonic-gate 
20217c478bd9Sstevel@tonic-gate 		/* key already in place */
20227c478bd9Sstevel@tonic-gate 		if ((encr_data.encrypt.state == ENCR_STATE_OK) &&
20237c478bd9Sstevel@tonic-gate 		    dir == TELNET_DIR_ENCRYPT && encr_data.encrypt.autoflag) {
20247c478bd9Sstevel@tonic-gate 			encrypt_start_output();
20257c478bd9Sstevel@tonic-gate 		}
20267c478bd9Sstevel@tonic-gate 		return;
20277c478bd9Sstevel@tonic-gate 	}
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate 	if (enc_debug)
20307c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(keyid) %s final state = %d\n",
20317c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
20327c478bd9Sstevel@tonic-gate 			    "DECRYPT"),
20337c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ?
20347c478bd9Sstevel@tonic-gate 			    encr_data.encrypt.state :
20357c478bd9Sstevel@tonic-gate 			    encr_data.decrypt.state));
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	encrypt_send_keyid(dir, newkeyid, *keyidlen, 0);
20387c478bd9Sstevel@tonic-gate }
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate /*
20417c478bd9Sstevel@tonic-gate  * encrypt_enc_keyid
20427c478bd9Sstevel@tonic-gate  *
20437c478bd9Sstevel@tonic-gate  * We received the ENC_KEYID message from a client indicating that
20447c478bd9Sstevel@tonic-gate  * the client wishes to verify that the indicated keyid maps to a
20457c478bd9Sstevel@tonic-gate  * valid key.
20467c478bd9Sstevel@tonic-gate  */
20477c478bd9Sstevel@tonic-gate static void
20487c478bd9Sstevel@tonic-gate encrypt_enc_keyid(char *data, int cnt)
20497c478bd9Sstevel@tonic-gate {
20507c478bd9Sstevel@tonic-gate 	/*
20517c478bd9Sstevel@tonic-gate 	 * Verify the decrypt keyid is valid
20527c478bd9Sstevel@tonic-gate 	 */
20537c478bd9Sstevel@tonic-gate 	encrypt_keyid(encr_data.decrypt.keyid, &encr_data.decrypt.keyidlen,
20547c478bd9Sstevel@tonic-gate 		    (uchar_t *)data, cnt, TELNET_DIR_DECRYPT);
20557c478bd9Sstevel@tonic-gate }
20567c478bd9Sstevel@tonic-gate 
20577c478bd9Sstevel@tonic-gate /*
20587c478bd9Sstevel@tonic-gate  * encrypt_dec_keyid
20597c478bd9Sstevel@tonic-gate  *
20607c478bd9Sstevel@tonic-gate  * We received the DEC_KEYID message from a client indicating that
20617c478bd9Sstevel@tonic-gate  * the client wants to verify that the indicated keyid maps to a valid key.
20627c478bd9Sstevel@tonic-gate  */
20637c478bd9Sstevel@tonic-gate static void
20647c478bd9Sstevel@tonic-gate encrypt_dec_keyid(char *data, int cnt)
20657c478bd9Sstevel@tonic-gate {
20667c478bd9Sstevel@tonic-gate 	encrypt_keyid(encr_data.encrypt.keyid, &encr_data.encrypt.keyidlen,
20677c478bd9Sstevel@tonic-gate 		    (uchar_t *)data, cnt, TELNET_DIR_ENCRYPT);
20687c478bd9Sstevel@tonic-gate }
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate /*
20717c478bd9Sstevel@tonic-gate  * encrypt_session_key
20727c478bd9Sstevel@tonic-gate  *
20737c478bd9Sstevel@tonic-gate  * Store the session key in the encryption data record
20747c478bd9Sstevel@tonic-gate  */
20757c478bd9Sstevel@tonic-gate static void
20767c478bd9Sstevel@tonic-gate encrypt_session_key(Session_Key *key, cipher_info_t *cinfo)
20777c478bd9Sstevel@tonic-gate {
20787c478bd9Sstevel@tonic-gate 	if (key == NULL || key->type != SK_DES) {
20797c478bd9Sstevel@tonic-gate 		if (enc_debug)
20807c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
20817c478bd9Sstevel@tonic-gate 				    "\t(session_key) Cannot set krb5 "
20827c478bd9Sstevel@tonic-gate 				    "session key (unknown type = %d)\n",
20837c478bd9Sstevel@tonic-gate 				    key ? key->type : -1);
20847c478bd9Sstevel@tonic-gate 	}
20857c478bd9Sstevel@tonic-gate 	if (enc_debug)
20867c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
20877c478bd9Sstevel@tonic-gate 			    "\t(session_key) Settting session key "
20887c478bd9Sstevel@tonic-gate 			    "for server\n");
20897c478bd9Sstevel@tonic-gate 
20907c478bd9Sstevel@tonic-gate 	/* store the key in the cipher info data struct */
20917c478bd9Sstevel@tonic-gate 	(void) memcpy(cinfo->krbdes_key, (void *)key->data, sizeof (Block));
20927c478bd9Sstevel@tonic-gate 
20937c478bd9Sstevel@tonic-gate 	/*
20947c478bd9Sstevel@tonic-gate 	 * Now look to see if we still need to send the key and start
20957c478bd9Sstevel@tonic-gate 	 * encrypting.
20967c478bd9Sstevel@tonic-gate 	 *
20977c478bd9Sstevel@tonic-gate 	 * If so, go ahead an call it now that we have the key.
20987c478bd9Sstevel@tonic-gate 	 */
20997c478bd9Sstevel@tonic-gate 	if (cinfo->need_start) {
21007c478bd9Sstevel@tonic-gate 		if (encrypt_send_encrypt_is() == ENCR_STATE_OK) {
21017c478bd9Sstevel@tonic-gate 			cinfo->need_start = 0;
21027c478bd9Sstevel@tonic-gate 		}
21037c478bd9Sstevel@tonic-gate 	}
21047c478bd9Sstevel@tonic-gate }
21057c478bd9Sstevel@tonic-gate 
21067c478bd9Sstevel@tonic-gate /*
21077c478bd9Sstevel@tonic-gate  * new_env
21087c478bd9Sstevel@tonic-gate  *
21097c478bd9Sstevel@tonic-gate  * Used to add an environment variable and value to the
21107c478bd9Sstevel@tonic-gate  * linked list structure.
21117c478bd9Sstevel@tonic-gate  */
21127c478bd9Sstevel@tonic-gate static int
21137c478bd9Sstevel@tonic-gate new_env(const char *name, const char *value)
21147c478bd9Sstevel@tonic-gate {
21157c478bd9Sstevel@tonic-gate 	struct envlist *env;
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 	env = malloc(sizeof (struct envlist));
21187c478bd9Sstevel@tonic-gate 	if (env == NULL)
21197c478bd9Sstevel@tonic-gate 		return (1);
21207c478bd9Sstevel@tonic-gate 	if ((env->name = strdup(name)) == NULL) {
21217c478bd9Sstevel@tonic-gate 		free(env);
21227c478bd9Sstevel@tonic-gate 		return (1);
21237c478bd9Sstevel@tonic-gate 	}
21247c478bd9Sstevel@tonic-gate 	if ((env->value = strdup(value)) == NULL) {
21257c478bd9Sstevel@tonic-gate 		free(env->name);
21267c478bd9Sstevel@tonic-gate 		free(env);
21277c478bd9Sstevel@tonic-gate 		return (1);
21287c478bd9Sstevel@tonic-gate 	}
21297c478bd9Sstevel@tonic-gate 	env->delete = 0;
21307c478bd9Sstevel@tonic-gate 	env->next = envlist_head;
21317c478bd9Sstevel@tonic-gate 	envlist_head = env;
21327c478bd9Sstevel@tonic-gate 	return (0);
21337c478bd9Sstevel@tonic-gate }
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate /*
21367c478bd9Sstevel@tonic-gate  * del_env
21377c478bd9Sstevel@tonic-gate  *
21387c478bd9Sstevel@tonic-gate  * Used to delete an environment variable from the linked list
21397c478bd9Sstevel@tonic-gate  * structure.  We just set a flag because we will delete the list
21407c478bd9Sstevel@tonic-gate  * anyway before we exec login.
21417c478bd9Sstevel@tonic-gate  */
21427c478bd9Sstevel@tonic-gate static int
21437c478bd9Sstevel@tonic-gate del_env(const char *name)
21447c478bd9Sstevel@tonic-gate {
21457c478bd9Sstevel@tonic-gate 	struct envlist *env;
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 	for (env = envlist_head; env; env = env->next) {
21487c478bd9Sstevel@tonic-gate 		if (strcmp(env->name, name) == 0) {
21497c478bd9Sstevel@tonic-gate 			env->delete = 1;
21507c478bd9Sstevel@tonic-gate 			break;
21517c478bd9Sstevel@tonic-gate 		}
21527c478bd9Sstevel@tonic-gate 	}
21537c478bd9Sstevel@tonic-gate 	return (0);
21547c478bd9Sstevel@tonic-gate }
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate static int
21577c478bd9Sstevel@tonic-gate issock(int fd)
21587c478bd9Sstevel@tonic-gate {
21597c478bd9Sstevel@tonic-gate 	struct stat stats;
21607c478bd9Sstevel@tonic-gate 
21617c478bd9Sstevel@tonic-gate 	if (fstat(fd, &stats) == -1)
21627c478bd9Sstevel@tonic-gate 		return (0);
21637c478bd9Sstevel@tonic-gate 	return (S_ISSOCK(stats.st_mode));
21647c478bd9Sstevel@tonic-gate }
21657c478bd9Sstevel@tonic-gate 
21667c478bd9Sstevel@tonic-gate /*
21677c478bd9Sstevel@tonic-gate  * audit_telnet_settid stores the terminal id while it is still
21687c478bd9Sstevel@tonic-gate  * available.  Subsequent calls to adt_load_hostname() return
21697c478bd9Sstevel@tonic-gate  * the id which is stored here.
21707c478bd9Sstevel@tonic-gate  */
21717c478bd9Sstevel@tonic-gate static int
21727c478bd9Sstevel@tonic-gate audit_telnet_settid(int sock) {
21737c478bd9Sstevel@tonic-gate 	adt_session_data_t	*ah;
21747c478bd9Sstevel@tonic-gate 	adt_termid_t		*termid;
21757c478bd9Sstevel@tonic-gate 	int			rc;
21767c478bd9Sstevel@tonic-gate 
21777c478bd9Sstevel@tonic-gate 	if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
21787c478bd9Sstevel@tonic-gate 		if ((rc = adt_load_termid(sock, &termid)) == 0) {
21797c478bd9Sstevel@tonic-gate 			if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
21807c478bd9Sstevel@tonic-gate 			    ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
21817c478bd9Sstevel@tonic-gate 			    termid, ADT_SETTID)) == 0)
21827c478bd9Sstevel@tonic-gate 				(void) adt_set_proc(ah);
21837c478bd9Sstevel@tonic-gate 			free(termid);
21847c478bd9Sstevel@tonic-gate 		}
21857c478bd9Sstevel@tonic-gate 		(void) adt_end_session(ah);
21867c478bd9Sstevel@tonic-gate 	}
21877c478bd9Sstevel@tonic-gate 	return (rc);
21887c478bd9Sstevel@tonic-gate }
21897c478bd9Sstevel@tonic-gate 
21907c478bd9Sstevel@tonic-gate /* ARGSUSED */
21917c478bd9Sstevel@tonic-gate int
21927c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
21937c478bd9Sstevel@tonic-gate {
21947c478bd9Sstevel@tonic-gate 	struct sockaddr_storage from;
21957c478bd9Sstevel@tonic-gate 	int on = 1;
21967c478bd9Sstevel@tonic-gate 	socklen_t fromlen;
21977c478bd9Sstevel@tonic-gate 	int issocket;
21987c478bd9Sstevel@tonic-gate #if	defined(DEBUG)
21997c478bd9Sstevel@tonic-gate 	ushort_t porttouse = 0;
22007c478bd9Sstevel@tonic-gate 	boolean_t standalone = 0;
22017c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
22027c478bd9Sstevel@tonic-gate 	extern char *optarg;
22037c478bd9Sstevel@tonic-gate 	char c;
22047c478bd9Sstevel@tonic-gate 	int tos = -1;
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, TELNETD_OPTS DEBUG_OPTS)) != -1) {
22077c478bd9Sstevel@tonic-gate 		switch (c) {
22087c478bd9Sstevel@tonic-gate #if defined(DEBUG)
22097c478bd9Sstevel@tonic-gate 		case 'p':
22107c478bd9Sstevel@tonic-gate 			/*
22117c478bd9Sstevel@tonic-gate 			 * note: alternative port number only used in
22127c478bd9Sstevel@tonic-gate 			 * standalone mode.
22137c478bd9Sstevel@tonic-gate 			 */
22147c478bd9Sstevel@tonic-gate 			porttouse = atoi(optarg);
22157c478bd9Sstevel@tonic-gate 			standalone = 1;
22167c478bd9Sstevel@tonic-gate 			break;
22177c478bd9Sstevel@tonic-gate 		case 'e':
22187c478bd9Sstevel@tonic-gate 			enc_debug = 1;
22197c478bd9Sstevel@tonic-gate 			break;
22207c478bd9Sstevel@tonic-gate #endif /* DEBUG */
22217c478bd9Sstevel@tonic-gate 		case 'a':
22227c478bd9Sstevel@tonic-gate 			if (strcasecmp(optarg, "none") == 0) {
22237c478bd9Sstevel@tonic-gate 				auth_level = 0;
22247c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "user") == 0) {
22257c478bd9Sstevel@tonic-gate 				auth_level = AUTH_USER;
22267c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "valid") == 0) {
22277c478bd9Sstevel@tonic-gate 				auth_level = AUTH_VALID;
22287c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "off") == 0) {
22297c478bd9Sstevel@tonic-gate 				auth_level = -1;
22307c478bd9Sstevel@tonic-gate 				negotiate_auth_krb5 = 0;
22317c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "debug") == 0) {
22327c478bd9Sstevel@tonic-gate 				auth_debug = 1;
22337c478bd9Sstevel@tonic-gate 			} else {
22347c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
22357c478bd9Sstevel@tonic-gate 				    "unknown authentication level specified "
22367c478bd9Sstevel@tonic-gate 				    "with \'-a\' option (%s)", optarg);
22377c478bd9Sstevel@tonic-gate 				auth_level = AUTH_USER;
22387c478bd9Sstevel@tonic-gate 			}
22397c478bd9Sstevel@tonic-gate 			break;
22407c478bd9Sstevel@tonic-gate 		case 'X':
22417c478bd9Sstevel@tonic-gate 			/* disable authentication negotiation */
22427c478bd9Sstevel@tonic-gate 			negotiate_auth_krb5 = 0;
22437c478bd9Sstevel@tonic-gate 			break;
22447c478bd9Sstevel@tonic-gate 		case 'R':
22457c478bd9Sstevel@tonic-gate 		case 'M':
22467c478bd9Sstevel@tonic-gate 			if (optarg != NULL) {
22477c478bd9Sstevel@tonic-gate 				int ret = krb5_init();
22487c478bd9Sstevel@tonic-gate 				if (ret) {
22497c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
22507c478bd9Sstevel@tonic-gate 						"Unable to use Kerberos V5 as "
22517c478bd9Sstevel@tonic-gate 						"requested, exiting");
22527c478bd9Sstevel@tonic-gate 					exit(1);
22537c478bd9Sstevel@tonic-gate 				}
22547c478bd9Sstevel@tonic-gate 				krb5_set_default_realm(telnet_context, optarg);
22557c478bd9Sstevel@tonic-gate 				syslog(LOG_NOTICE,
22567c478bd9Sstevel@tonic-gate 				    "using %s as default KRB5 realm", optarg);
22577c478bd9Sstevel@tonic-gate 			}
22587c478bd9Sstevel@tonic-gate 			break;
22597c478bd9Sstevel@tonic-gate 		case 'S':
22607c478bd9Sstevel@tonic-gate 			telnet_srvtab = (char *)strdup(optarg);
22617c478bd9Sstevel@tonic-gate 			break;
22627c478bd9Sstevel@tonic-gate 		case 'E': /* disable automatic encryption */
22637c478bd9Sstevel@tonic-gate 			negotiate_encrypt = B_FALSE;
22647c478bd9Sstevel@tonic-gate 			break;
22657c478bd9Sstevel@tonic-gate 		case 'U':
22667c478bd9Sstevel@tonic-gate 			resolve_hostname = 1;
22677c478bd9Sstevel@tonic-gate 			break;
22687c478bd9Sstevel@tonic-gate 		case 's':
22697c478bd9Sstevel@tonic-gate 			if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
22707c478bd9Sstevel@tonic-gate 			    tos > 255) {
22717c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR, "telnetd: illegal tos value: "
22727c478bd9Sstevel@tonic-gate 				    "%s\n", optarg);
22737c478bd9Sstevel@tonic-gate 			} else {
22747c478bd9Sstevel@tonic-gate 				if (tos < 0)
22757c478bd9Sstevel@tonic-gate 					tos = 020;
22767c478bd9Sstevel@tonic-gate 			}
22777c478bd9Sstevel@tonic-gate 			break;
22787c478bd9Sstevel@tonic-gate 		case 'h':
22797c478bd9Sstevel@tonic-gate 			show_hostinfo = 0;
22807c478bd9Sstevel@tonic-gate 			break;
22817c478bd9Sstevel@tonic-gate 		default:
22827c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "telnetd: illegal cmd line option %c",
22837c478bd9Sstevel@tonic-gate 			    c);
22847c478bd9Sstevel@tonic-gate 			break;
22857c478bd9Sstevel@tonic-gate 		}
22867c478bd9Sstevel@tonic-gate 	}
22877c478bd9Sstevel@tonic-gate 
22887c478bd9Sstevel@tonic-gate 	netibufsize = BUFSIZ;
22897c478bd9Sstevel@tonic-gate 	if (!(netibuf = (char *)malloc(netibufsize)))
22907c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "netibuf malloc failed\n");
22917c478bd9Sstevel@tonic-gate 	(void) memset(netibuf, 0, netibufsize);
22927c478bd9Sstevel@tonic-gate 	netip = netibuf;
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate #if	defined(DEBUG)
22957c478bd9Sstevel@tonic-gate 	if (standalone) {
22967c478bd9Sstevel@tonic-gate 		int s, ns, foo;
22977c478bd9Sstevel@tonic-gate 		struct servent *sp;
22987c478bd9Sstevel@tonic-gate 		static struct sockaddr_in6 sin6 = { AF_INET6 };
22997c478bd9Sstevel@tonic-gate 		int option = 1;
23007c478bd9Sstevel@tonic-gate 
23017c478bd9Sstevel@tonic-gate 		if (porttouse) {
23027c478bd9Sstevel@tonic-gate 			sin6.sin6_port = htons(porttouse);
23037c478bd9Sstevel@tonic-gate 		} else {
23047c478bd9Sstevel@tonic-gate 			sp = getservbyname("telnet", "tcp");
23057c478bd9Sstevel@tonic-gate 			if (sp == 0) {
23067c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
23077c478bd9Sstevel@tonic-gate 					    "telnetd: tcp/telnet: "
23087c478bd9Sstevel@tonic-gate 					    "unknown service\n");
23097c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
23107c478bd9Sstevel@tonic-gate 			}
23117c478bd9Sstevel@tonic-gate 			sin6.sin6_port = sp->s_port;
23127c478bd9Sstevel@tonic-gate 		}
23137c478bd9Sstevel@tonic-gate 
23147c478bd9Sstevel@tonic-gate 		s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
23157c478bd9Sstevel@tonic-gate 		if (s < 0) {
23167c478bd9Sstevel@tonic-gate 			perror("telnetd: socket");
23177c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
23187c478bd9Sstevel@tonic-gate 		}
23197c478bd9Sstevel@tonic-gate 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
23207c478bd9Sstevel@tonic-gate 		    sizeof (option)) == -1)
23217c478bd9Sstevel@tonic-gate 			perror("setsockopt SO_REUSEADDR");
23227c478bd9Sstevel@tonic-gate 		if (bind(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
23237c478bd9Sstevel@tonic-gate 			perror("bind");
23247c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
23257c478bd9Sstevel@tonic-gate 		}
23267c478bd9Sstevel@tonic-gate 		if (listen(s, 32) < 0) {
23277c478bd9Sstevel@tonic-gate 			perror("listen");
23287c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
23297c478bd9Sstevel@tonic-gate 		}
23307c478bd9Sstevel@tonic-gate 
23317c478bd9Sstevel@tonic-gate 		/* automatically reap all child processes */
23327c478bd9Sstevel@tonic-gate 		(void) signal(SIGCHLD, SIG_IGN);
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate 		for (;;) {
23357c478bd9Sstevel@tonic-gate 			pid_t pid;
23367c478bd9Sstevel@tonic-gate 
23377c478bd9Sstevel@tonic-gate 			foo = sizeof (sin6);
23387c478bd9Sstevel@tonic-gate 			ns = accept(s, (struct sockaddr *)&sin6, &foo);
23397c478bd9Sstevel@tonic-gate 			if (ns < 0) {
23407c478bd9Sstevel@tonic-gate 				perror("accept");
23417c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
23427c478bd9Sstevel@tonic-gate 			}
23437c478bd9Sstevel@tonic-gate 			pid = fork();
23447c478bd9Sstevel@tonic-gate 			if (pid == -1) {
23457c478bd9Sstevel@tonic-gate 				perror("fork");
23467c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
23477c478bd9Sstevel@tonic-gate 			}
23487c478bd9Sstevel@tonic-gate 			if (pid == 0) {
23497c478bd9Sstevel@tonic-gate 				(void) dup2(ns, 0);
23507c478bd9Sstevel@tonic-gate 				(void) close(s);
23517c478bd9Sstevel@tonic-gate 				(void) signal(SIGCHLD, SIG_DFL);
23527c478bd9Sstevel@tonic-gate 				break;
23537c478bd9Sstevel@tonic-gate 			}
23547c478bd9Sstevel@tonic-gate 			(void) close(ns);
23557c478bd9Sstevel@tonic-gate 		}
23567c478bd9Sstevel@tonic-gate 	}
23577c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
23607c478bd9Sstevel@tonic-gate 
23617c478bd9Sstevel@tonic-gate 	issocket = issock(0);
23627c478bd9Sstevel@tonic-gate 	if (!issocket)
23637c478bd9Sstevel@tonic-gate 		fatal(0, "stdin is not a socket file descriptor");
23647c478bd9Sstevel@tonic-gate 
23657c478bd9Sstevel@tonic-gate 	fromlen = (socklen_t)sizeof (from);
23667c478bd9Sstevel@tonic-gate 	(void) memset((char *)&from, 0, sizeof (from));
23677c478bd9Sstevel@tonic-gate 	if (getpeername(0, (struct sockaddr *)&from, &fromlen)
23687c478bd9Sstevel@tonic-gate 	    < 0) {
23697c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", argv[0]);
23707c478bd9Sstevel@tonic-gate 		perror("getpeername");
23717c478bd9Sstevel@tonic-gate 		_exit(EXIT_FAILURE);
23727c478bd9Sstevel@tonic-gate 	}
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate 	if (audit_telnet_settid(0)) {	/* set terminal ID */
23757c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", argv[0]);
23767c478bd9Sstevel@tonic-gate 		perror("audit");
23777c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
23787c478bd9Sstevel@tonic-gate 	}
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on,
23817c478bd9Sstevel@tonic-gate 						sizeof (on)) < 0) {
23827c478bd9Sstevel@tonic-gate 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
23837c478bd9Sstevel@tonic-gate 	}
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	/*
23867c478bd9Sstevel@tonic-gate 	 * Set the TOS value
23877c478bd9Sstevel@tonic-gate 	 */
23887c478bd9Sstevel@tonic-gate 	if (tos != -1 &&
23897c478bd9Sstevel@tonic-gate 	    setsockopt(0, IPPROTO_IP, IP_TOS,
23907c478bd9Sstevel@tonic-gate 		    (char *)&tos, sizeof (tos)) < 0 &&
23917c478bd9Sstevel@tonic-gate 		errno != ENOPROTOOPT) {
23927c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m", tos);
23937c478bd9Sstevel@tonic-gate 	}
23947c478bd9Sstevel@tonic-gate 
23957c478bd9Sstevel@tonic-gate 	if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
23967c478bd9Sstevel@tonic-gate 	    sizeof (on)) < 0) {
23977c478bd9Sstevel@tonic-gate 		syslog(LOG_WARNING, "setsockopt (SO_OOBINLINE): %m");
23987c478bd9Sstevel@tonic-gate 	}
23997c478bd9Sstevel@tonic-gate 
24007c478bd9Sstevel@tonic-gate 	/* set the default PAM service name */
24017c478bd9Sstevel@tonic-gate 	(void) strcpy(pam_svc_name, "telnet");
24027c478bd9Sstevel@tonic-gate 
24037c478bd9Sstevel@tonic-gate 	doit(0, &from);
24047c478bd9Sstevel@tonic-gate 	return (EXIT_SUCCESS);
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate 
24077c478bd9Sstevel@tonic-gate static char	*terminaltype = 0;
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate /*
24107c478bd9Sstevel@tonic-gate  * ttloop
24117c478bd9Sstevel@tonic-gate  *
24127c478bd9Sstevel@tonic-gate  *	A small subroutine to flush the network output buffer, get some data
24137c478bd9Sstevel@tonic-gate  * from the network, and pass it through the telnet state machine.  We
24147c478bd9Sstevel@tonic-gate  * also flush the pty input buffer (by dropping its data) if it becomes
24157c478bd9Sstevel@tonic-gate  * too full.
24167c478bd9Sstevel@tonic-gate  */
24177c478bd9Sstevel@tonic-gate static void
24187c478bd9Sstevel@tonic-gate ttloop(void)
24197c478bd9Sstevel@tonic-gate {
24207c478bd9Sstevel@tonic-gate 	if (nfrontp-nbackp) {
24217c478bd9Sstevel@tonic-gate 		netflush();
24227c478bd9Sstevel@tonic-gate 	}
24237c478bd9Sstevel@tonic-gate read_again:
24247c478bd9Sstevel@tonic-gate 	ncc = read(net, netibuf, netibufsize);
24257c478bd9Sstevel@tonic-gate 	if (ncc < 0) {
24267c478bd9Sstevel@tonic-gate 		if (errno == EINTR)
24277c478bd9Sstevel@tonic-gate 			goto read_again;
24287c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ttloop:  read: %m");
24297c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
24307c478bd9Sstevel@tonic-gate 	} else if (ncc == 0) {
24317c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ttloop:  peer closed connection\n");
24327c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
24337c478bd9Sstevel@tonic-gate 	}
24347c478bd9Sstevel@tonic-gate 
24357c478bd9Sstevel@tonic-gate 	netip = netibuf;
24367c478bd9Sstevel@tonic-gate 	telrcv();		/* state machine */
24377c478bd9Sstevel@tonic-gate 	if (ncc > 0) {
24387c478bd9Sstevel@tonic-gate 		pfrontp = pbackp = ptyobuf;
24397c478bd9Sstevel@tonic-gate 		telrcv();
24407c478bd9Sstevel@tonic-gate 	}
24417c478bd9Sstevel@tonic-gate }
24427c478bd9Sstevel@tonic-gate 
24437c478bd9Sstevel@tonic-gate static void
24447c478bd9Sstevel@tonic-gate send_do(int option)
24457c478bd9Sstevel@tonic-gate {
24467c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)DO, (uchar_t)option);
24477c478bd9Sstevel@tonic-gate }
24487c478bd9Sstevel@tonic-gate 
24497c478bd9Sstevel@tonic-gate static void
24507c478bd9Sstevel@tonic-gate send_will(int option)
24517c478bd9Sstevel@tonic-gate {
24527c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WILL, (uchar_t)option);
24537c478bd9Sstevel@tonic-gate }
24547c478bd9Sstevel@tonic-gate 
24557c478bd9Sstevel@tonic-gate static void
24567c478bd9Sstevel@tonic-gate send_wont(int option)
24577c478bd9Sstevel@tonic-gate {
24587c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WONT, (uchar_t)option);
24597c478bd9Sstevel@tonic-gate }
24607c478bd9Sstevel@tonic-gate 
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate /*
24637c478bd9Sstevel@tonic-gate  * getauthtype
24647c478bd9Sstevel@tonic-gate  *
24657c478bd9Sstevel@tonic-gate  * Negotiate automatic authentication, is possible.
24667c478bd9Sstevel@tonic-gate  */
24677c478bd9Sstevel@tonic-gate static int
24687c478bd9Sstevel@tonic-gate getauthtype(char *username, int *len)
24697c478bd9Sstevel@tonic-gate {
24707c478bd9Sstevel@tonic-gate 	int init_status = -1;
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 	init_status = krb5_init();
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 	if (auth_level == -1 || init_status != 0) {
24757c478bd9Sstevel@tonic-gate 		remopts[TELOPT_AUTHENTICATION] = OPT_NO;
24767c478bd9Sstevel@tonic-gate 		myopts[TELOPT_AUTHENTICATION] = OPT_NO;
24777c478bd9Sstevel@tonic-gate 		negotiate_auth_krb5 = B_FALSE;
24787c478bd9Sstevel@tonic-gate 		negotiate_encrypt = B_FALSE;
24797c478bd9Sstevel@tonic-gate 		return (AUTH_REJECT);
24807c478bd9Sstevel@tonic-gate 	}
24817c478bd9Sstevel@tonic-gate 
24827c478bd9Sstevel@tonic-gate 	if (init_status == 0 && auth_level != -1) {
24837c478bd9Sstevel@tonic-gate 		if (negotiate_auth_krb5) {
24847c478bd9Sstevel@tonic-gate 			/*
24857c478bd9Sstevel@tonic-gate 			 * Negotiate Authentication FIRST
24867c478bd9Sstevel@tonic-gate 			 */
24877c478bd9Sstevel@tonic-gate 			send_do(TELOPT_AUTHENTICATION);
24887c478bd9Sstevel@tonic-gate 			remopts[TELOPT_AUTHENTICATION] =
24897c478bd9Sstevel@tonic-gate 				OPT_YES_BUT_ALWAYS_LOOK;
24907c478bd9Sstevel@tonic-gate 		}
24917c478bd9Sstevel@tonic-gate 		while (sequenceIs(authopt, getauth))
24927c478bd9Sstevel@tonic-gate 			ttloop();
24937c478bd9Sstevel@tonic-gate 
24947c478bd9Sstevel@tonic-gate 		if (remopts[TELOPT_AUTHENTICATION] == OPT_YES) {
24957c478bd9Sstevel@tonic-gate 			/*
24967c478bd9Sstevel@tonic-gate 			 * Request KRB5 Mutual authentication and if that fails,
24977c478bd9Sstevel@tonic-gate 			 * KRB5 1-way client authentication
24987c478bd9Sstevel@tonic-gate 			 */
24997c478bd9Sstevel@tonic-gate 			uchar_t sbbuf[MAXOPTLEN], *p;
25007c478bd9Sstevel@tonic-gate 			p = sbbuf;
25017c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)IAC;
25027c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)SB;
25037c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)TELOPT_AUTHENTICATION;
25047c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)TELQUAL_SEND;
25057c478bd9Sstevel@tonic-gate 			if (negotiate_auth_krb5) {
25067c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25077c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT |
25087c478bd9Sstevel@tonic-gate 						AUTH_HOW_MUTUAL |
25097c478bd9Sstevel@tonic-gate 						AUTH_ENCRYPT_ON);
25107c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25117c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT |
25127c478bd9Sstevel@tonic-gate 						AUTH_HOW_MUTUAL);
25137c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25147c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT|
25157c478bd9Sstevel@tonic-gate 						AUTH_HOW_ONE_WAY);
25167c478bd9Sstevel@tonic-gate 			} else {
25177c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_NULL;
25187c478bd9Sstevel@tonic-gate 			}
25197c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)IAC;
25207c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)SE;
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 			write_data_len((const char *)sbbuf,
25237c478bd9Sstevel@tonic-gate 				    (size_t)(p - sbbuf));
25247c478bd9Sstevel@tonic-gate 			netflush();
25257c478bd9Sstevel@tonic-gate 			if (auth_debug)
25267c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
25277c478bd9Sstevel@tonic-gate 					    "SENT TELOPT_AUTHENTICATION "
25287c478bd9Sstevel@tonic-gate 					    "[data]\n");
25297c478bd9Sstevel@tonic-gate 
25307c478bd9Sstevel@tonic-gate 			/* auth_wait returns the authentication level */
25317c478bd9Sstevel@tonic-gate 			/* status = auth_wait(username, len); */
25327c478bd9Sstevel@tonic-gate 			while (sequenceIs(authdone, getauth))
25337c478bd9Sstevel@tonic-gate 				ttloop();
25347c478bd9Sstevel@tonic-gate 			/*
25357c478bd9Sstevel@tonic-gate 			 * Now check to see if the user is valid or not
25367c478bd9Sstevel@tonic-gate 			 */
25377c478bd9Sstevel@tonic-gate 			if (authenticated == NULL || authenticated == &NoAuth)
25387c478bd9Sstevel@tonic-gate 				auth_status = AUTH_REJECT;
25397c478bd9Sstevel@tonic-gate 			else {
25407c478bd9Sstevel@tonic-gate 				/*
25417c478bd9Sstevel@tonic-gate 				 * We cant be VALID until the user status is
25427c478bd9Sstevel@tonic-gate 				 * checked.
25437c478bd9Sstevel@tonic-gate 				 */
25447c478bd9Sstevel@tonic-gate 				if (auth_status == AUTH_VALID)
25457c478bd9Sstevel@tonic-gate 					auth_status = AUTH_USER;
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 				if (authenticated->AuthName ==
25487c478bd9Sstevel@tonic-gate 					AUTHTYPE_KERBEROS_V5)
25497c478bd9Sstevel@tonic-gate 					auth_status = krb5_user_status(
25507c478bd9Sstevel@tonic-gate 						username, *len, auth_status);
25517c478bd9Sstevel@tonic-gate 			}
25527c478bd9Sstevel@tonic-gate 		}
25537c478bd9Sstevel@tonic-gate 	}
25547c478bd9Sstevel@tonic-gate 	return (auth_status);
25557c478bd9Sstevel@tonic-gate }
25567c478bd9Sstevel@tonic-gate 
25577c478bd9Sstevel@tonic-gate static void
25587c478bd9Sstevel@tonic-gate getencrtype(void)
25597c478bd9Sstevel@tonic-gate {
25607c478bd9Sstevel@tonic-gate 	if (krb5_privacy_allowed() && negotiate_encrypt) {
25617c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ENCRYPT] != OPT_YES) {
25627c478bd9Sstevel@tonic-gate 			if (!sent_will_encrypt) {
25637c478bd9Sstevel@tonic-gate 				send_will(TELOPT_ENCRYPT);
25647c478bd9Sstevel@tonic-gate 				sent_will_encrypt = B_TRUE;
25657c478bd9Sstevel@tonic-gate 			}
25667c478bd9Sstevel@tonic-gate 			if (enc_debug)
25677c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "SENT WILL ENCRYPT\n");
25687c478bd9Sstevel@tonic-gate 		}
25697c478bd9Sstevel@tonic-gate 		if (remopts[TELOPT_ENCRYPT] != OPT_YES) {
25707c478bd9Sstevel@tonic-gate 			if (!sent_do_encrypt) {
25717c478bd9Sstevel@tonic-gate 				send_do(TELOPT_ENCRYPT);
25727c478bd9Sstevel@tonic-gate 				sent_do_encrypt = B_TRUE;
25733ff1fd9dSethindra 				remopts[TELOPT_ENCRYPT] =
25743ff1fd9dSethindra 				    OPT_YES_BUT_ALWAYS_LOOK;
25757c478bd9Sstevel@tonic-gate 			}
25767c478bd9Sstevel@tonic-gate 			if (enc_debug)
25777c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "SENT DO ENCRYPT\n");
25787c478bd9Sstevel@tonic-gate 		}
25797c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = OPT_YES;
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate 		while (sequenceIs(encropt, getencr))
25827c478bd9Sstevel@tonic-gate 		    ttloop();
25837c478bd9Sstevel@tonic-gate 
25847c478bd9Sstevel@tonic-gate 		if (auth_status != AUTH_REJECT &&
25857c478bd9Sstevel@tonic-gate 		    remopts[TELOPT_ENCRYPT] == OPT_YES &&
25867c478bd9Sstevel@tonic-gate 		    myopts[TELOPT_ENCRYPT] == OPT_YES) {
25877c478bd9Sstevel@tonic-gate 
25887c478bd9Sstevel@tonic-gate 			if (sent_encrypt_support == B_FALSE) {
25897c478bd9Sstevel@tonic-gate 				write_data("%c%c%c%c%c%c%c",
25907c478bd9Sstevel@tonic-gate 					(uchar_t)IAC,
25917c478bd9Sstevel@tonic-gate 					(uchar_t)SB,
25927c478bd9Sstevel@tonic-gate 					(uchar_t)TELOPT_ENCRYPT,
25937c478bd9Sstevel@tonic-gate 					(uchar_t)ENCRYPT_SUPPORT,
25947c478bd9Sstevel@tonic-gate 					(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
25957c478bd9Sstevel@tonic-gate 					(uchar_t)IAC,
25967c478bd9Sstevel@tonic-gate 					(uchar_t)SE);
25977c478bd9Sstevel@tonic-gate 
25987c478bd9Sstevel@tonic-gate 				netflush();
25997c478bd9Sstevel@tonic-gate 			}
26007c478bd9Sstevel@tonic-gate 			/*
26017c478bd9Sstevel@tonic-gate 			 * Now wait for a response to these messages before
26027c478bd9Sstevel@tonic-gate 			 * continuing...
26037c478bd9Sstevel@tonic-gate 			 * Look for TELOPT_ENCRYPT suboptions
26047c478bd9Sstevel@tonic-gate 			 */
26057c478bd9Sstevel@tonic-gate 			while (sequenceIs(encr_support, getencr))
26067c478bd9Sstevel@tonic-gate 				ttloop();
26077c478bd9Sstevel@tonic-gate 		}
26087c478bd9Sstevel@tonic-gate 	} else {
26097c478bd9Sstevel@tonic-gate 		/* Dont need responses to these, so dont wait for them */
26107c478bd9Sstevel@tonic-gate 		settimer(encropt);
26117c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] = OPT_NO;
26127c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = OPT_NO;
26137c478bd9Sstevel@tonic-gate 	}
26147c478bd9Sstevel@tonic-gate 
26157c478bd9Sstevel@tonic-gate }
26167c478bd9Sstevel@tonic-gate 
26177c478bd9Sstevel@tonic-gate /*
26187c478bd9Sstevel@tonic-gate  * getterminaltype
26197c478bd9Sstevel@tonic-gate  *
26207c478bd9Sstevel@tonic-gate  * Ask the other end to send along its terminal type.
26217c478bd9Sstevel@tonic-gate  * Output is the variable terminaltype filled in.
26227c478bd9Sstevel@tonic-gate  */
26237c478bd9Sstevel@tonic-gate static void
26247c478bd9Sstevel@tonic-gate getterminaltype(void)
26257c478bd9Sstevel@tonic-gate {
26267c478bd9Sstevel@tonic-gate 	/*
26277c478bd9Sstevel@tonic-gate 	 * The remote side may have already sent this info, so
26287c478bd9Sstevel@tonic-gate 	 * dont ask for these options if the other side already
26297c478bd9Sstevel@tonic-gate 	 * sent the information.
26307c478bd9Sstevel@tonic-gate 	 */
26317c478bd9Sstevel@tonic-gate 	if (sequenceIs(ttypeopt, getterminal)) {
26327c478bd9Sstevel@tonic-gate 		send_do(TELOPT_TTYPE);
26337c478bd9Sstevel@tonic-gate 		remopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
26347c478bd9Sstevel@tonic-gate 	}
26357c478bd9Sstevel@tonic-gate 
26367c478bd9Sstevel@tonic-gate 	if (sequenceIs(nawsopt, getterminal)) {
26377c478bd9Sstevel@tonic-gate 		send_do(TELOPT_NAWS);
26387c478bd9Sstevel@tonic-gate 		remopts[TELOPT_NAWS] = OPT_YES_BUT_ALWAYS_LOOK;
26397c478bd9Sstevel@tonic-gate 	}
26407c478bd9Sstevel@tonic-gate 
26417c478bd9Sstevel@tonic-gate 	if (sequenceIs(xdisplocopt, getterminal)) {
26427c478bd9Sstevel@tonic-gate 		send_do(TELOPT_XDISPLOC);
26437c478bd9Sstevel@tonic-gate 		remopts[TELOPT_XDISPLOC] = OPT_YES_BUT_ALWAYS_LOOK;
26447c478bd9Sstevel@tonic-gate 	}
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	if (sequenceIs(environopt, getterminal)) {
26477c478bd9Sstevel@tonic-gate 		send_do(TELOPT_NEW_ENVIRON);
26487c478bd9Sstevel@tonic-gate 		remopts[TELOPT_NEW_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
26497c478bd9Sstevel@tonic-gate 	}
26507c478bd9Sstevel@tonic-gate 
26517c478bd9Sstevel@tonic-gate 	if (sequenceIs(oenvironopt, getterminal)) {
26527c478bd9Sstevel@tonic-gate 		send_do(TELOPT_OLD_ENVIRON);
26537c478bd9Sstevel@tonic-gate 		remopts[TELOPT_OLD_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
26547c478bd9Sstevel@tonic-gate 	}
26557c478bd9Sstevel@tonic-gate 
26567c478bd9Sstevel@tonic-gate 	/* make sure encryption is started here */
26577c478bd9Sstevel@tonic-gate 	while (auth_status != AUTH_REJECT &&
26587c478bd9Sstevel@tonic-gate 		authenticated != &NoAuth && authenticated != NULL &&
26597c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] == OPT_YES &&
26607c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag &&
26617c478bd9Sstevel@tonic-gate 		encr_data.encrypt.state != ENCR_STATE_OK) {
26627c478bd9Sstevel@tonic-gate 	    if (enc_debug)
26637c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "getterminaltype() forcing encrypt\n");
26647c478bd9Sstevel@tonic-gate 	    ttloop();
26657c478bd9Sstevel@tonic-gate 	}
26667c478bd9Sstevel@tonic-gate 
26677c478bd9Sstevel@tonic-gate 	if (enc_debug) {
26687c478bd9Sstevel@tonic-gate 	    (void) fprintf(stderr, "getterminaltype() encryption %sstarted\n",
26697c478bd9Sstevel@tonic-gate 		    encr_data.encrypt.state == ENCR_STATE_OK ? "" : "not ");
26707c478bd9Sstevel@tonic-gate 	}
26717c478bd9Sstevel@tonic-gate 
26727c478bd9Sstevel@tonic-gate 	while (sequenceIs(ttypeopt, getterminal) ||
26737c478bd9Sstevel@tonic-gate 	    sequenceIs(nawsopt, getterminal) ||
26747c478bd9Sstevel@tonic-gate 	    sequenceIs(xdisplocopt, getterminal) ||
26757c478bd9Sstevel@tonic-gate 	    sequenceIs(environopt, getterminal) ||
26767c478bd9Sstevel@tonic-gate 	    sequenceIs(oenvironopt, getterminal)) {
26777c478bd9Sstevel@tonic-gate 		ttloop();
26787c478bd9Sstevel@tonic-gate 	}
26797c478bd9Sstevel@tonic-gate 
26807c478bd9Sstevel@tonic-gate 
26817c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_TTYPE] == OPT_YES) {
26827c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26837c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_TTYPE, (uchar_t)TELQUAL_SEND,
26847c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
26857c478bd9Sstevel@tonic-gate 
26867c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
26877c478bd9Sstevel@tonic-gate 	}
26887c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
26897c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26907c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_XDISPLOC, (uchar_t)TELQUAL_SEND,
26917c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
26927c478bd9Sstevel@tonic-gate 
26937c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
26947c478bd9Sstevel@tonic-gate 	}
26957c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
26967c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26977c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_NEW_ENVIRON, (uchar_t)TELQUAL_SEND,
26987c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
26997c478bd9Sstevel@tonic-gate 
27007c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
27017c478bd9Sstevel@tonic-gate 	}
27027c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
27037c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
27047c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_OLD_ENVIRON, (uchar_t)TELQUAL_SEND,
27057c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
27087c478bd9Sstevel@tonic-gate 	}
27097c478bd9Sstevel@tonic-gate 
27107c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_TTYPE] == OPT_YES) {
27117c478bd9Sstevel@tonic-gate 		while (sequenceIs(ttypesubopt, getterminal)) {
27127c478bd9Sstevel@tonic-gate 			ttloop();
27137c478bd9Sstevel@tonic-gate 		}
27147c478bd9Sstevel@tonic-gate 	}
27157c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
27167c478bd9Sstevel@tonic-gate 		while (sequenceIs(xdisplocsubopt, getterminal)) {
27177c478bd9Sstevel@tonic-gate 			ttloop();
27187c478bd9Sstevel@tonic-gate 		}
27197c478bd9Sstevel@tonic-gate 	}
27207c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
27217c478bd9Sstevel@tonic-gate 		while (sequenceIs(environsubopt, getterminal)) {
27227c478bd9Sstevel@tonic-gate 			ttloop();
27237c478bd9Sstevel@tonic-gate 		}
27247c478bd9Sstevel@tonic-gate 	}
27257c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
27267c478bd9Sstevel@tonic-gate 		while (sequenceIs(oenvironsubopt, getterminal)) {
27277c478bd9Sstevel@tonic-gate 			ttloop();
27287c478bd9Sstevel@tonic-gate 		}
27297c478bd9Sstevel@tonic-gate 	}
27307c478bd9Sstevel@tonic-gate 	init_neg_done = 1;
27317c478bd9Sstevel@tonic-gate }
27327c478bd9Sstevel@tonic-gate 
27337c478bd9Sstevel@tonic-gate pid_t pid;
27347c478bd9Sstevel@tonic-gate 
27357c478bd9Sstevel@tonic-gate /*
27367c478bd9Sstevel@tonic-gate  * Get a pty, scan input lines.
27377c478bd9Sstevel@tonic-gate  */
27387c478bd9Sstevel@tonic-gate static void
27397c478bd9Sstevel@tonic-gate doit(int f, struct sockaddr_storage *who)
27407c478bd9Sstevel@tonic-gate {
27417c478bd9Sstevel@tonic-gate 	char *host;
27427c478bd9Sstevel@tonic-gate 	char host_name[MAXHOSTNAMELEN];
27437c478bd9Sstevel@tonic-gate 	int p, t, tt;
27447c478bd9Sstevel@tonic-gate 	struct sgttyb b;
27457c478bd9Sstevel@tonic-gate 	int	ptmfd;	/* fd of logindmux connected to pty */
27467c478bd9Sstevel@tonic-gate 	int	netfd;	/* fd of logindmux connected to netf */
27477c478bd9Sstevel@tonic-gate 	struct	stat	buf;
27487c478bd9Sstevel@tonic-gate 	struct	protocol_arg	telnetp;
27497c478bd9Sstevel@tonic-gate 	struct	strioctl	telnetmod;
27507c478bd9Sstevel@tonic-gate 	struct	envlist	*env, *next;
27517c478bd9Sstevel@tonic-gate 	int	nsize = 0;
27527c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
27537c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
27547c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
27557c478bd9Sstevel@tonic-gate 	socklen_t wholen;
27567c478bd9Sstevel@tonic-gate 	char username[MAXUSERNAMELEN];
27577c478bd9Sstevel@tonic-gate 	int len;
27587c478bd9Sstevel@tonic-gate 	uchar_t passthru;
27597c478bd9Sstevel@tonic-gate 	char *slavename;
27607c478bd9Sstevel@tonic-gate 
27617c478bd9Sstevel@tonic-gate 	if ((p = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) {
27627c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/ptmx", errno);
27637c478bd9Sstevel@tonic-gate 	}
27647c478bd9Sstevel@tonic-gate 	if (grantpt(p) == -1)
27657c478bd9Sstevel@tonic-gate 		fatal(f, "could not grant slave pty");
27667c478bd9Sstevel@tonic-gate 	if (unlockpt(p) == -1)
27677c478bd9Sstevel@tonic-gate 		fatal(f, "could not unlock slave pty");
27687c478bd9Sstevel@tonic-gate 	if ((slavename = ptsname(p)) == NULL)
27697c478bd9Sstevel@tonic-gate 		fatal(f, "could not enable slave pty");
27707c478bd9Sstevel@tonic-gate 	(void) dup2(f, 0);
27717c478bd9Sstevel@tonic-gate 	if ((t = open(slavename, O_RDWR | O_NOCTTY)) == -1)
27727c478bd9Sstevel@tonic-gate 		fatal(f, "could not open slave pty");
27737c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ptem") == -1)
27747c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ptem", errno);
27757c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ldterm") == -1)
27767c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ldterm", errno);
27777c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ttcompat") == -1)
27787c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ttcompat", errno);
27797c478bd9Sstevel@tonic-gate 
27807c478bd9Sstevel@tonic-gate 	line = slavename;
27817c478bd9Sstevel@tonic-gate 
27827c478bd9Sstevel@tonic-gate 	pty = t;
27837c478bd9Sstevel@tonic-gate 
27847c478bd9Sstevel@tonic-gate 	if (ioctl(t, TIOCGETP, &b) == -1)
27857c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP pty t: %m\n");
27867c478bd9Sstevel@tonic-gate 	b.sg_flags = O_CRMOD|O_XTABS|O_ANYP;
27877c478bd9Sstevel@tonic-gate 	/* XXX - ispeed and ospeed must be non-zero */
27887c478bd9Sstevel@tonic-gate 	b.sg_ispeed = B38400;
27897c478bd9Sstevel@tonic-gate 	b.sg_ospeed = B38400;
27907c478bd9Sstevel@tonic-gate 	if (ioctl(t, TIOCSETN, &b) == -1)
27917c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCSETN pty t: %m\n");
27927c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCGETP, &b) == -1)
27937c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP pty pty: %m\n");
27947c478bd9Sstevel@tonic-gate 	b.sg_flags &= ~O_ECHO;
27957c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCSETN, &b) == -1)
27967c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCSETN pty pty: %m\n");
27977c478bd9Sstevel@tonic-gate 
27987c478bd9Sstevel@tonic-gate 	if (who->ss_family == AF_INET) {
27997c478bd9Sstevel@tonic-gate 		char *addrbuf = NULL;
28007c478bd9Sstevel@tonic-gate 		char *portbuf = NULL;
28017c478bd9Sstevel@tonic-gate 
28027c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)who;
28037c478bd9Sstevel@tonic-gate 		wholen = sizeof (struct sockaddr_in);
28047c478bd9Sstevel@tonic-gate 
28057c478bd9Sstevel@tonic-gate 		addrbuf = (char *)malloc(wholen);
28067c478bd9Sstevel@tonic-gate 		if (addrbuf == NULL)
28077c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for address info\n");
28087c478bd9Sstevel@tonic-gate 		portbuf = (char *)malloc(sizeof (sin->sin_port));
28097c478bd9Sstevel@tonic-gate 		if (portbuf == NULL) {
28107c478bd9Sstevel@tonic-gate 			free(addrbuf);
28117c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for port info\n");
28127c478bd9Sstevel@tonic-gate 		}
28137c478bd9Sstevel@tonic-gate 
28147c478bd9Sstevel@tonic-gate 		(void) memcpy(addrbuf, (const void *)&sin->sin_addr, wholen);
28157c478bd9Sstevel@tonic-gate 		(void) memcpy(portbuf, (const void *)&sin->sin_port,
28167c478bd9Sstevel@tonic-gate 			    sizeof (sin->sin_port));
28177c478bd9Sstevel@tonic-gate 
28187c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
28197c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
28207c478bd9Sstevel@tonic-gate 
28217c478bd9Sstevel@tonic-gate 		rsaddr.contents = (krb5_octet *)addrbuf;
28227c478bd9Sstevel@tonic-gate 		rsaddr.length = wholen;
28237c478bd9Sstevel@tonic-gate 		rsaddr.addrtype = ADDRTYPE_INET;
28247c478bd9Sstevel@tonic-gate 
28257c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
28267c478bd9Sstevel@tonic-gate 			free(rsport.contents);
28277c478bd9Sstevel@tonic-gate 
28287c478bd9Sstevel@tonic-gate 		rsport.contents = (krb5_octet *)portbuf;
28297c478bd9Sstevel@tonic-gate 		rsport.length = sizeof (sin->sin_port);
28307c478bd9Sstevel@tonic-gate 		rsport.addrtype = ADDRTYPE_IPPORT;
28317c478bd9Sstevel@tonic-gate 	} else if (who->ss_family == AF_INET6) {
28327c478bd9Sstevel@tonic-gate 		struct in_addr ipv4_addr;
28337c478bd9Sstevel@tonic-gate 		char *addrbuf = NULL;
28347c478bd9Sstevel@tonic-gate 		char *portbuf = NULL;
28357c478bd9Sstevel@tonic-gate 
28367c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)who;
28377c478bd9Sstevel@tonic-gate 		wholen = sizeof (struct sockaddr_in6);
28387c478bd9Sstevel@tonic-gate 
28397c478bd9Sstevel@tonic-gate 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
28407c478bd9Sstevel@tonic-gate 				    &ipv4_addr);
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 		addrbuf = (char *)malloc(wholen);
28437c478bd9Sstevel@tonic-gate 		if (addrbuf == NULL)
28447c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for address info\n");
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate 		portbuf = (char *)malloc(sizeof (sin6->sin6_port));
28477c478bd9Sstevel@tonic-gate 		if (portbuf == NULL) {
28487c478bd9Sstevel@tonic-gate 			free(addrbuf);
28497c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for port info\n");
28507c478bd9Sstevel@tonic-gate 		}
28517c478bd9Sstevel@tonic-gate 
28527c478bd9Sstevel@tonic-gate 		(void) memcpy((void *) addrbuf,
28537c478bd9Sstevel@tonic-gate 			    (const void *)&ipv4_addr,
28547c478bd9Sstevel@tonic-gate 			    wholen);
28557c478bd9Sstevel@tonic-gate 		/*
28567c478bd9Sstevel@tonic-gate 		 * If we already used rsaddr.contents, free the previous
28577c478bd9Sstevel@tonic-gate 		 * buffer.
28587c478bd9Sstevel@tonic-gate 		 */
28597c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
28607c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
28617c478bd9Sstevel@tonic-gate 
28627c478bd9Sstevel@tonic-gate 		rsaddr.contents = (krb5_octet *)addrbuf;
28637c478bd9Sstevel@tonic-gate 		rsaddr.length = sizeof (ipv4_addr);
28647c478bd9Sstevel@tonic-gate 		rsaddr.addrtype = ADDRTYPE_INET;
28657c478bd9Sstevel@tonic-gate 
28667c478bd9Sstevel@tonic-gate 		(void) memcpy((void *) portbuf, (const void *)&sin6->sin6_port,
28677c478bd9Sstevel@tonic-gate 			    sizeof (sin6->sin6_port));
28687c478bd9Sstevel@tonic-gate 
28697c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
28707c478bd9Sstevel@tonic-gate 			free(rsport.contents);
28717c478bd9Sstevel@tonic-gate 
28727c478bd9Sstevel@tonic-gate 		rsport.contents = (krb5_octet *)portbuf;
28737c478bd9Sstevel@tonic-gate 		rsport.length = sizeof (sin6->sin6_port);
28747c478bd9Sstevel@tonic-gate 		rsport.addrtype = ADDRTYPE_IPPORT;
28757c478bd9Sstevel@tonic-gate 	} else {
28767c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unknown address family %d\n",
28777c478bd9Sstevel@tonic-gate 		    who->ss_family);
28787c478bd9Sstevel@tonic-gate 		fatal(f, "getpeername: unknown address family\n");
28797c478bd9Sstevel@tonic-gate 	}
28807c478bd9Sstevel@tonic-gate 
28817c478bd9Sstevel@tonic-gate 	if (getnameinfo((const struct sockaddr *) who, wholen, host_name,
28827c478bd9Sstevel@tonic-gate 	    sizeof (host_name), NULL, 0, 0) == 0) {
28837c478bd9Sstevel@tonic-gate 		host = host_name;
28847c478bd9Sstevel@tonic-gate 	} else {
28857c478bd9Sstevel@tonic-gate 		/*
28867c478bd9Sstevel@tonic-gate 		 * If the '-U' option was given on the cmd line, we must
28877c478bd9Sstevel@tonic-gate 		 * be able to lookup the hostname
28887c478bd9Sstevel@tonic-gate 		 */
28897c478bd9Sstevel@tonic-gate 		if (resolve_hostname) {
28907c478bd9Sstevel@tonic-gate 			fatal(f, "Couldn't resolve your address into a "
28917c478bd9Sstevel@tonic-gate 			    "host name.\r\nPlease contact your net "
28927c478bd9Sstevel@tonic-gate 			    "administrator");
28937c478bd9Sstevel@tonic-gate 		}
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 		if (who->ss_family == AF_INET6) {
28967c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
28977c478bd9Sstevel@tonic-gate 				struct in_addr ipv4_addr;
28987c478bd9Sstevel@tonic-gate 
28997c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
29007c478bd9Sstevel@tonic-gate 				    &ipv4_addr);
29017c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET,
29027c478bd9Sstevel@tonic-gate 				    &ipv4_addr, abuf, sizeof (abuf));
29037c478bd9Sstevel@tonic-gate 			} else {
29047c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET6,
29057c478bd9Sstevel@tonic-gate 				    &sin6->sin6_addr, abuf,
29067c478bd9Sstevel@tonic-gate 				    sizeof (abuf));
29077c478bd9Sstevel@tonic-gate 			}
29087c478bd9Sstevel@tonic-gate 		} else if (who->ss_family == AF_INET) {
29097c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET,
29107c478bd9Sstevel@tonic-gate 				    &sin->sin_addr, abuf, sizeof (abuf));
29117c478bd9Sstevel@tonic-gate 			}
29127c478bd9Sstevel@tonic-gate 	}
29137c478bd9Sstevel@tonic-gate 	/*
29147c478bd9Sstevel@tonic-gate 	 * Note that sockmod has to be removed since readstream assumes
29157c478bd9Sstevel@tonic-gate 	 * a "raw" TPI endpoint (e.g. it uses getmsg).
29167c478bd9Sstevel@tonic-gate 	 */
29177c478bd9Sstevel@tonic-gate 	if (removemod(f, "sockmod") < 0)
29187c478bd9Sstevel@tonic-gate 		fatalperror(f, "couldn't remove sockmod", errno);
29197c478bd9Sstevel@tonic-gate 
29207c478bd9Sstevel@tonic-gate 	encrypt_init();
29217c478bd9Sstevel@tonic-gate 
29227c478bd9Sstevel@tonic-gate 	/*
29237c478bd9Sstevel@tonic-gate 	 * Push the crypto module on the stream before 'telmod' so it
29247c478bd9Sstevel@tonic-gate 	 * can encrypt/decrypt without interfering with telmod functionality
29257c478bd9Sstevel@tonic-gate 	 * We must push it now because many of the crypto options negotiated
29267c478bd9Sstevel@tonic-gate 	 * initially must be saved in the crypto module (via IOCTL calls).
29277c478bd9Sstevel@tonic-gate 	 */
29287c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_PUSH, "cryptmod") < 0)
29297c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH cryptmod", errno);
29307c478bd9Sstevel@tonic-gate 
29317c478bd9Sstevel@tonic-gate 	cryptmod_fd = f;
29327c478bd9Sstevel@tonic-gate 	/*
29337c478bd9Sstevel@tonic-gate 	 * gotta set the encryption clock now because it is often negotiated
29347c478bd9Sstevel@tonic-gate 	 * immediately by the client, and if we wait till after we negotiate
29357c478bd9Sstevel@tonic-gate 	 * auth, it will be out of whack with when the WILL/WONT ENCRYPT
29367c478bd9Sstevel@tonic-gate 	 * option is received.
29377c478bd9Sstevel@tonic-gate 	 */
29387c478bd9Sstevel@tonic-gate 	settimer(getencr);
29397c478bd9Sstevel@tonic-gate 
29407c478bd9Sstevel@tonic-gate 	/*
29417c478bd9Sstevel@tonic-gate 	 * get terminal type.
29427c478bd9Sstevel@tonic-gate 	 */
29437c478bd9Sstevel@tonic-gate 	username[0] = '\0';
29447c478bd9Sstevel@tonic-gate 	len = sizeof (username);
29457c478bd9Sstevel@tonic-gate 
29467c478bd9Sstevel@tonic-gate 	settimer(getterminal);
29477c478bd9Sstevel@tonic-gate 	settimer(getauth);
29487c478bd9Sstevel@tonic-gate 	/*
29497c478bd9Sstevel@tonic-gate 	 * Exchange TELOPT_AUTHENTICATE options per RFC 2941/2942
29507c478bd9Sstevel@tonic-gate 	 */
29517c478bd9Sstevel@tonic-gate 	auth_status = getauthtype(username, &len);
29527c478bd9Sstevel@tonic-gate 	/*
29537c478bd9Sstevel@tonic-gate 	 * Exchange TELOPT_ENCRYPT options per RFC 2946
29547c478bd9Sstevel@tonic-gate 	 */
29557c478bd9Sstevel@tonic-gate 	getencrtype();
29567c478bd9Sstevel@tonic-gate 	getterminaltype();
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_PUSH, "telmod") < 0)
29597c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH telmod", errno);
29607c478bd9Sstevel@tonic-gate 
29617c478bd9Sstevel@tonic-gate 	/*
29627c478bd9Sstevel@tonic-gate 	 * Make sure telmod will pass unrecognized IOCTLs to cryptmod
29637c478bd9Sstevel@tonic-gate 	 */
29647c478bd9Sstevel@tonic-gate 	passthru = 1;
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = CRYPTPASSTHRU;
29677c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
29687c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (uchar_t);
29697c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&passthru;
29707c478bd9Sstevel@tonic-gate 
29717c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_STR, &telnetmod) < 0)
29727c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl CRPASSTHRU failed\n");
29737c478bd9Sstevel@tonic-gate 
29747c478bd9Sstevel@tonic-gate 	if (!ncc)
29757c478bd9Sstevel@tonic-gate 		netip = netibuf;
29767c478bd9Sstevel@tonic-gate 
29777c478bd9Sstevel@tonic-gate 	/*
29787c478bd9Sstevel@tonic-gate 	 * readstream will do a getmsg till it receives M_PROTO type
29797c478bd9Sstevel@tonic-gate 	 * T_DATA_REQ from telnetmodopen().  This signals that all data
29807c478bd9Sstevel@tonic-gate 	 * in-flight before telmod was pushed has been received at the
29817c478bd9Sstevel@tonic-gate 	 * stream head.
29827c478bd9Sstevel@tonic-gate 	 */
29837c478bd9Sstevel@tonic-gate 	while ((nsize = readstream(f, netibuf, ncc + netip - netibuf)) > 0) {
29847c478bd9Sstevel@tonic-gate 		ncc += nsize;
29857c478bd9Sstevel@tonic-gate 	}
29867c478bd9Sstevel@tonic-gate 
29877c478bd9Sstevel@tonic-gate 	if (nsize < 0) {
29887c478bd9Sstevel@tonic-gate 		fatalperror(f, "readstream failed\n", errno);
29897c478bd9Sstevel@tonic-gate 	}
29907c478bd9Sstevel@tonic-gate 
29917c478bd9Sstevel@tonic-gate 	/*
29927c478bd9Sstevel@tonic-gate 	 * open logindmux drivers and link them with network and ptm
29937c478bd9Sstevel@tonic-gate 	 * file descriptors.
29947c478bd9Sstevel@tonic-gate 	 */
29957c478bd9Sstevel@tonic-gate 	if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1) {
29967c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/logindmux", errno);
29977c478bd9Sstevel@tonic-gate 	}
29987c478bd9Sstevel@tonic-gate 	if ((netfd = open("/dev/logindmux", O_RDWR)) == -1) {
29997c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/logindmux", errno);
30007c478bd9Sstevel@tonic-gate 	}
30017c478bd9Sstevel@tonic-gate 
30027c478bd9Sstevel@tonic-gate 	if (ioctl(ptmfd, I_LINK, p) < 0)
30037c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
30047c478bd9Sstevel@tonic-gate 	if (ioctl(netfd, I_LINK, f) < 0)
30057c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl I_LINK of tcp connection failed\n");
30067c478bd9Sstevel@tonic-gate 
30077c478bd9Sstevel@tonic-gate 	/*
30087c478bd9Sstevel@tonic-gate 	 * Figure out the device number of ptm's mux fd, and pass that
30097c478bd9Sstevel@tonic-gate 	 * to the net's mux.
30107c478bd9Sstevel@tonic-gate 	 */
30117c478bd9Sstevel@tonic-gate 	if (fstat(ptmfd, &buf) < 0) {
30127c478bd9Sstevel@tonic-gate 		fatalperror(f, "fstat ptmfd failed", errno);
30137c478bd9Sstevel@tonic-gate 	}
30147c478bd9Sstevel@tonic-gate 	telnetp.dev = buf.st_rdev;
30157c478bd9Sstevel@tonic-gate 	telnetp.flag = 0;
30167c478bd9Sstevel@tonic-gate 
30177c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
30187c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
30197c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (struct protocol_arg);
30207c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&telnetp;
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate 	if (ioctl(netfd, I_STR, &telnetmod) < 0)
30237c478bd9Sstevel@tonic-gate 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
30247c478bd9Sstevel@tonic-gate 
30257c478bd9Sstevel@tonic-gate 	/*
30267c478bd9Sstevel@tonic-gate 	 * Figure out the device number of the net's mux fd, and pass that
30277c478bd9Sstevel@tonic-gate 	 * to the ptm's mux.
30287c478bd9Sstevel@tonic-gate 	 */
30297c478bd9Sstevel@tonic-gate 	if (fstat(netfd, &buf) < 0) {
30307c478bd9Sstevel@tonic-gate 		fatalperror(f, "fstat netfd failed", errno);
30317c478bd9Sstevel@tonic-gate 	}
30327c478bd9Sstevel@tonic-gate 	telnetp.dev = buf.st_rdev;
30337c478bd9Sstevel@tonic-gate 	telnetp.flag = 1;
30347c478bd9Sstevel@tonic-gate 
30357c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
30367c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
30377c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (struct protocol_arg);
30387c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&telnetp;
30397c478bd9Sstevel@tonic-gate 
30407c478bd9Sstevel@tonic-gate 	if (ioctl(ptmfd, I_STR, &telnetmod) < 0)
30417c478bd9Sstevel@tonic-gate 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of ptmfd failed\n");
30427c478bd9Sstevel@tonic-gate 
30437c478bd9Sstevel@tonic-gate 	net = netfd;
30447c478bd9Sstevel@tonic-gate 	master = ptmfd;
30457c478bd9Sstevel@tonic-gate 	cryptmod_fd = netfd;
30467c478bd9Sstevel@tonic-gate 
30477c478bd9Sstevel@tonic-gate 	/*
30487c478bd9Sstevel@tonic-gate 	 * Show banner that getty never gave, but
30497c478bd9Sstevel@tonic-gate 	 * only if the user did not automatically authenticate.
30507c478bd9Sstevel@tonic-gate 	 */
30517c478bd9Sstevel@tonic-gate 	if (getenv("USER") == '\0' && auth_status < AUTH_USER)
30527c478bd9Sstevel@tonic-gate 		showbanner();
30537c478bd9Sstevel@tonic-gate 
30547c478bd9Sstevel@tonic-gate 	/*
30557c478bd9Sstevel@tonic-gate 	 * If the user automatically authenticated with Kerberos
30567c478bd9Sstevel@tonic-gate 	 * we must set the service name that PAM will use.  We
30577c478bd9Sstevel@tonic-gate 	 * need to do it BEFORE the child fork so that 'cleanup'
30587c478bd9Sstevel@tonic-gate 	 * in the parent can call the PAM cleanup stuff with the
30597c478bd9Sstevel@tonic-gate 	 * same PAM service that /bin/login will use to authenticate
30607c478bd9Sstevel@tonic-gate 	 * this session.
30617c478bd9Sstevel@tonic-gate 	 */
30627c478bd9Sstevel@tonic-gate 	if (auth_level >= 0 && auth_status >= AUTH_USER &&
30637c478bd9Sstevel@tonic-gate 	    (AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) {
30647c478bd9Sstevel@tonic-gate 		(void) strcpy(pam_svc_name, "ktelnet");
30657c478bd9Sstevel@tonic-gate 	}
30667c478bd9Sstevel@tonic-gate 	/*
30677c478bd9Sstevel@tonic-gate 	 * Request to do suppress go ahead.
30687c478bd9Sstevel@tonic-gate 	 *
30697c478bd9Sstevel@tonic-gate 	 * Send this before sending the TELOPT_ECHO stuff below because
30707c478bd9Sstevel@tonic-gate 	 * some clients (MIT KRB5 telnet) have quirky 'kludge mode' support
30717c478bd9Sstevel@tonic-gate 	 * that has them turn off local echo mode if SGA is not received first.
30727c478bd9Sstevel@tonic-gate 	 * This also has the odd side-effect of causing the client to enable
30737c478bd9Sstevel@tonic-gate 	 * encryption and then immediately disable it during the ECHO option
30747c478bd9Sstevel@tonic-gate 	 * negotiations.  Its just better to to SGA first now that we support
30757c478bd9Sstevel@tonic-gate 	 * encryption.
30767c478bd9Sstevel@tonic-gate 	 */
30777c478bd9Sstevel@tonic-gate 	if (!myopts[TELOPT_SGA]) {
30787c478bd9Sstevel@tonic-gate 	    dooption(TELOPT_SGA);
30797c478bd9Sstevel@tonic-gate 	}
30807c478bd9Sstevel@tonic-gate 
30817c478bd9Sstevel@tonic-gate 	/*
30827c478bd9Sstevel@tonic-gate 	 * Pretend we got a DO ECHO from the client if we have not
30837c478bd9Sstevel@tonic-gate 	 * yet negotiated the ECHO.
30847c478bd9Sstevel@tonic-gate 	 */
30857c478bd9Sstevel@tonic-gate 	if (!myopts[TELOPT_ECHO]) {
30867c478bd9Sstevel@tonic-gate 	    dooption(TELOPT_ECHO);
30877c478bd9Sstevel@tonic-gate 	}
30887c478bd9Sstevel@tonic-gate 
30897c478bd9Sstevel@tonic-gate 	/*
30907c478bd9Sstevel@tonic-gate 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
30917c478bd9Sstevel@tonic-gate 	 * because 4.2 clients are unable to deal with TCP urgent data.
30927c478bd9Sstevel@tonic-gate 	 *
30937c478bd9Sstevel@tonic-gate 	 * To find out, we send out a "DO ECHO".  If the remote system
30947c478bd9Sstevel@tonic-gate 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
30957c478bd9Sstevel@tonic-gate 	 * that fact ("WILL ECHO" ==> that the client will echo what
30967c478bd9Sstevel@tonic-gate 	 * WE, the server, sends it; it does NOT mean that the client will
30977c478bd9Sstevel@tonic-gate 	 * echo the terminal input).
30987c478bd9Sstevel@tonic-gate 	 */
30997c478bd9Sstevel@tonic-gate 	send_do(TELOPT_ECHO);
31007c478bd9Sstevel@tonic-gate 	remopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	if ((pid = fork()) < 0)
31037c478bd9Sstevel@tonic-gate 		fatalperror(netfd, "fork", errno);
31047c478bd9Sstevel@tonic-gate 	if (pid)
31057c478bd9Sstevel@tonic-gate 		telnet(net, master);
31067c478bd9Sstevel@tonic-gate 	/*
31077c478bd9Sstevel@tonic-gate 	 * The child process needs to be the session leader
31087c478bd9Sstevel@tonic-gate 	 * and have the pty as its controlling tty.  Thus we need
31097c478bd9Sstevel@tonic-gate 	 * to re-open the slave side of the pty no without
31107c478bd9Sstevel@tonic-gate 	 * the O_NOCTTY flag that we have been careful to
31117c478bd9Sstevel@tonic-gate 	 * use up to this point.
31127c478bd9Sstevel@tonic-gate 	 */
31137c478bd9Sstevel@tonic-gate 	(void) setsid();
31147c478bd9Sstevel@tonic-gate 
31157c478bd9Sstevel@tonic-gate 	tt = open(line, O_RDWR);
31167c478bd9Sstevel@tonic-gate 	if (tt < 0)
31177c478bd9Sstevel@tonic-gate 		fatalperror(netfd, line, errno);
31187c478bd9Sstevel@tonic-gate 	(void) close(netfd);
31197c478bd9Sstevel@tonic-gate 	(void) close(ptmfd);
31207c478bd9Sstevel@tonic-gate 	(void) close(f);
31217c478bd9Sstevel@tonic-gate 	(void) close(p);
31227c478bd9Sstevel@tonic-gate 	(void) close(t);
31237c478bd9Sstevel@tonic-gate 	if (tt != 0)
31247c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 0);
31257c478bd9Sstevel@tonic-gate 	if (tt != 1)
31267c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 1);
31277c478bd9Sstevel@tonic-gate 	if (tt != 2)
31287c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 2);
31297c478bd9Sstevel@tonic-gate 	if (tt > 2)
31307c478bd9Sstevel@tonic-gate 		(void) close(tt);
31317c478bd9Sstevel@tonic-gate 
31327c478bd9Sstevel@tonic-gate 	if (terminaltype)
31337c478bd9Sstevel@tonic-gate 		(void) local_setenv("TERM", terminaltype+5, 1);
31347c478bd9Sstevel@tonic-gate 	/*
31357c478bd9Sstevel@tonic-gate 	 * 	-h : pass on name of host.
31367c478bd9Sstevel@tonic-gate 	 *		WARNING:  -h is accepted by login if and only if
31377c478bd9Sstevel@tonic-gate 	 *			getuid() == 0.
31387c478bd9Sstevel@tonic-gate 	 * 	-p : don't clobber the environment (so terminal type stays set).
31397c478bd9Sstevel@tonic-gate 	 */
31407c478bd9Sstevel@tonic-gate 	{
31417c478bd9Sstevel@tonic-gate 		/* System V login expects a utmp entry to already be there */
31427c478bd9Sstevel@tonic-gate 		struct utmpx ut;
31437c478bd9Sstevel@tonic-gate 		(void) memset((char *)&ut, 0, sizeof (ut));
31447c478bd9Sstevel@tonic-gate 		(void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
31457c478bd9Sstevel@tonic-gate 		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
31467c478bd9Sstevel@tonic-gate 		ut.ut_pid = getpid();
31477c478bd9Sstevel@tonic-gate 		ut.ut_id[0] = 't';
31487c478bd9Sstevel@tonic-gate 		ut.ut_id[1] = (char)SC_WILDC;
31497c478bd9Sstevel@tonic-gate 		ut.ut_id[2] = (char)SC_WILDC;
31507c478bd9Sstevel@tonic-gate 		ut.ut_id[3] = (char)SC_WILDC;
31517c478bd9Sstevel@tonic-gate 		ut.ut_type = LOGIN_PROCESS;
31527c478bd9Sstevel@tonic-gate 		ut.ut_exit.e_termination = 0;
31537c478bd9Sstevel@tonic-gate 		ut.ut_exit.e_exit = 0;
31547c478bd9Sstevel@tonic-gate 		(void) time(&ut.ut_tv.tv_sec);
31557c478bd9Sstevel@tonic-gate 		if (makeutx(&ut) == NULL)
31567c478bd9Sstevel@tonic-gate 			syslog(LOG_INFO, "in.telnetd:\tmakeutx failed");
31577c478bd9Sstevel@tonic-gate 	}
31587c478bd9Sstevel@tonic-gate 
31597c478bd9Sstevel@tonic-gate 	/*
31607c478bd9Sstevel@tonic-gate 	 * Load in the cached environment variables and either
31617c478bd9Sstevel@tonic-gate 	 * set/unset them in the environment.
31627c478bd9Sstevel@tonic-gate 	 */
31637c478bd9Sstevel@tonic-gate 	for (next = envlist_head; next; ) {
31647c478bd9Sstevel@tonic-gate 		env = next;
31657c478bd9Sstevel@tonic-gate 		if (env->delete)
31667c478bd9Sstevel@tonic-gate 			(void) local_unsetenv(env->name);
31677c478bd9Sstevel@tonic-gate 		else
31687c478bd9Sstevel@tonic-gate 			(void) local_setenv(env->name, env->value, 1);
31697c478bd9Sstevel@tonic-gate 		free(env->name);
31707c478bd9Sstevel@tonic-gate 		free(env->value);
31717c478bd9Sstevel@tonic-gate 		next = env->next;
31727c478bd9Sstevel@tonic-gate 		free(env);
31737c478bd9Sstevel@tonic-gate 	}
31747c478bd9Sstevel@tonic-gate 
31757c478bd9Sstevel@tonic-gate 	if (!username || !username[0])
31767c478bd9Sstevel@tonic-gate 		auth_status = AUTH_REJECT; /* we dont know who this is */
31777c478bd9Sstevel@tonic-gate 
31787c478bd9Sstevel@tonic-gate 	/* If the current auth status is less than the required level, exit */
31797c478bd9Sstevel@tonic-gate 	if (auth_status < auth_level) {
31807c478bd9Sstevel@tonic-gate 		fatal(net, "Authentication failed\n");
31817c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
31827c478bd9Sstevel@tonic-gate 	}
31837c478bd9Sstevel@tonic-gate 
31847c478bd9Sstevel@tonic-gate 	/*
31857c478bd9Sstevel@tonic-gate 	 * If AUTH_VALID (proper authentication REQUIRED and we have
31867c478bd9Sstevel@tonic-gate 	 * a krb5_name), exec '/bin/login', make sure it uses the
31877c478bd9Sstevel@tonic-gate 	 * correct PAM service name (pam_svc_name). If possible,
31887c478bd9Sstevel@tonic-gate 	 * make sure the krb5 authenticated user's name (krb5_name)
31897c478bd9Sstevel@tonic-gate 	 * is in the PAM REPOSITORY for krb5.
31907c478bd9Sstevel@tonic-gate 	 */
31917c478bd9Sstevel@tonic-gate 	if (auth_level >= 0 &&
31927c478bd9Sstevel@tonic-gate 	    (auth_status == AUTH_VALID || auth_status == AUTH_USER) &&
31937c478bd9Sstevel@tonic-gate 	    ((krb5_name != NULL) && strlen(krb5_name)) &&
31947c478bd9Sstevel@tonic-gate 	    ((AuthenticatingUser != NULL) && strlen(AuthenticatingUser))) {
31957c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
31967c478bd9Sstevel@tonic-gate 			    "-p",
31977c478bd9Sstevel@tonic-gate 			    "-d", slavename,
31987c478bd9Sstevel@tonic-gate 			    "-h", host,
31997c478bd9Sstevel@tonic-gate 			    "-u", krb5_name,
32007c478bd9Sstevel@tonic-gate 			    "-s", pam_svc_name,
32017c478bd9Sstevel@tonic-gate 			    "-R", KRB5_REPOSITORY_NAME,
32027c478bd9Sstevel@tonic-gate 			    AuthenticatingUser, 0);
32037c478bd9Sstevel@tonic-gate 	} else if (auth_level >= 0 &&
32047c478bd9Sstevel@tonic-gate 		auth_status >= AUTH_USER &&
32057c478bd9Sstevel@tonic-gate 		(((AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) ||
32067c478bd9Sstevel@tonic-gate 		getenv("USER"))) {
32077c478bd9Sstevel@tonic-gate 		/*
32087c478bd9Sstevel@tonic-gate 		 * If we only know the name but not the principal,
32097c478bd9Sstevel@tonic-gate 		 * login will have to authenticate further.
32107c478bd9Sstevel@tonic-gate 		 */
32117c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
32127c478bd9Sstevel@tonic-gate 		    "-p",
32137c478bd9Sstevel@tonic-gate 		    "-d", slavename,
32147c478bd9Sstevel@tonic-gate 		    "-h", host,
3215e1c3e95aSdanmcd 		    "-s", pam_svc_name, "--",
32167c478bd9Sstevel@tonic-gate 		    (AuthenticatingUser != NULL ? AuthenticatingUser :
3217e1c3e95aSdanmcd 			getenv("USER")), 0);
32187c478bd9Sstevel@tonic-gate 
32197c478bd9Sstevel@tonic-gate 	} else /* default, no auth. info available, login does it all */ {
32207c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
3221e1c3e95aSdanmcd 		    "-p", "-h", host, "-d", slavename, "--",
32227c478bd9Sstevel@tonic-gate 		    getenv("USER"), 0);
32237c478bd9Sstevel@tonic-gate 	}
32247c478bd9Sstevel@tonic-gate 
32257c478bd9Sstevel@tonic-gate 	fatalperror(netfd, LOGIN_PROGRAM, errno);
32267c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
32277c478bd9Sstevel@tonic-gate }
32287c478bd9Sstevel@tonic-gate 
32297c478bd9Sstevel@tonic-gate static void
32307c478bd9Sstevel@tonic-gate fatal(int f, char *msg)
32317c478bd9Sstevel@tonic-gate {
32327c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
32337c478bd9Sstevel@tonic-gate 
32347c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "telnetd: %s.\r\n", msg);
32357c478bd9Sstevel@tonic-gate 	(void) write(f, buf, strlen(buf));
32367c478bd9Sstevel@tonic-gate 	exit(EXIT_FAILURE);
32377c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
32387c478bd9Sstevel@tonic-gate }
32397c478bd9Sstevel@tonic-gate 
32407c478bd9Sstevel@tonic-gate static void
32417c478bd9Sstevel@tonic-gate fatalperror(int f, char *msg, int errnum)
32427c478bd9Sstevel@tonic-gate {
32437c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
32467c478bd9Sstevel@tonic-gate 			"%s: %s\r\n", msg, strerror(errnum));
32477c478bd9Sstevel@tonic-gate 	fatal(f, buf);
32487c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
32497c478bd9Sstevel@tonic-gate }
32507c478bd9Sstevel@tonic-gate 
32517c478bd9Sstevel@tonic-gate /*
32527c478bd9Sstevel@tonic-gate  * Main loop.  Select from pty and network, and
32537c478bd9Sstevel@tonic-gate  * hand data to telnet receiver finite state machine
32547c478bd9Sstevel@tonic-gate  * when it receives telnet protocol. Regular data
32557c478bd9Sstevel@tonic-gate  * flow between pty and network takes place through
32567c478bd9Sstevel@tonic-gate  * inkernel telnet streams module (telmod).
32577c478bd9Sstevel@tonic-gate  */
32587c478bd9Sstevel@tonic-gate static void
32597c478bd9Sstevel@tonic-gate telnet(int net, int master)
32607c478bd9Sstevel@tonic-gate {
32617c478bd9Sstevel@tonic-gate 	int on = 1;
32627c478bd9Sstevel@tonic-gate 	char mode;
32637c478bd9Sstevel@tonic-gate 	struct	strioctl	telnetmod;
32647c478bd9Sstevel@tonic-gate 	int	nsize = 0;
32657c478bd9Sstevel@tonic-gate 	char	binary_in = 0;
32667c478bd9Sstevel@tonic-gate 	char binary_out = 0;
32677c478bd9Sstevel@tonic-gate 
32687c478bd9Sstevel@tonic-gate 	if (ioctl(net, FIONBIO, &on) == -1)
32697c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl FIONBIO net: %m\n");
32707c478bd9Sstevel@tonic-gate 	if (ioctl(master, FIONBIO, &on) == -1)
32717c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl FIONBIO pty p: %m\n");
32727c478bd9Sstevel@tonic-gate 	(void) signal(SIGTSTP, SIG_IGN);
32737c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, (void (*)())cleanup);
32747c478bd9Sstevel@tonic-gate 	(void) setpgrp();
32757c478bd9Sstevel@tonic-gate 
32767c478bd9Sstevel@tonic-gate 	/*
32777c478bd9Sstevel@tonic-gate 	 * Call telrcv() once to pick up anything received during
32787c478bd9Sstevel@tonic-gate 	 * terminal type negotiation.
32797c478bd9Sstevel@tonic-gate 	 */
32807c478bd9Sstevel@tonic-gate 	telrcv();
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate 	netflush();
32837c478bd9Sstevel@tonic-gate 	ptyflush();
32847c478bd9Sstevel@tonic-gate 
32857c478bd9Sstevel@tonic-gate 	for (;;) {
32867c478bd9Sstevel@tonic-gate 		fd_set ibits, obits, xbits;
32877c478bd9Sstevel@tonic-gate 		int c;
32887c478bd9Sstevel@tonic-gate 
32897c478bd9Sstevel@tonic-gate 		if (ncc < 0)
32907c478bd9Sstevel@tonic-gate 			break;
32917c478bd9Sstevel@tonic-gate 
32927c478bd9Sstevel@tonic-gate 		FD_ZERO(&ibits);
32937c478bd9Sstevel@tonic-gate 		FD_ZERO(&obits);
32947c478bd9Sstevel@tonic-gate 		FD_ZERO(&xbits);
32957c478bd9Sstevel@tonic-gate 
32967c478bd9Sstevel@tonic-gate 		/*
32977c478bd9Sstevel@tonic-gate 		 * If we couldn't flush all our output to the network,
32987c478bd9Sstevel@tonic-gate 		 * keep checking for when we can.
32997c478bd9Sstevel@tonic-gate 		 */
33007c478bd9Sstevel@tonic-gate 		if (nfrontp - nbackp)
33017c478bd9Sstevel@tonic-gate 			FD_SET(net, &obits);
33027c478bd9Sstevel@tonic-gate 		/*
33037c478bd9Sstevel@tonic-gate 		 * Never look for input if there's still
33047c478bd9Sstevel@tonic-gate 		 * stuff in the corresponding output buffer
33057c478bd9Sstevel@tonic-gate 		 */
33067c478bd9Sstevel@tonic-gate 		if (pfrontp - pbackp) {
33077c478bd9Sstevel@tonic-gate 			FD_SET(master, &obits);
33087c478bd9Sstevel@tonic-gate 		} else {
33097c478bd9Sstevel@tonic-gate 			FD_SET(net, &ibits);
33107c478bd9Sstevel@tonic-gate 		}
33117c478bd9Sstevel@tonic-gate 		if (!SYNCHing) {
33127c478bd9Sstevel@tonic-gate 			FD_SET(net, &xbits);
33137c478bd9Sstevel@tonic-gate 		}
33147c478bd9Sstevel@tonic-gate 
33157c478bd9Sstevel@tonic-gate #define	max(x, y)	(((x) < (y)) ? (y) : (x))
33167c478bd9Sstevel@tonic-gate 
33177c478bd9Sstevel@tonic-gate 		/*
33187c478bd9Sstevel@tonic-gate 		 * make an ioctl to telnet module (net side) to send
33197c478bd9Sstevel@tonic-gate 		 * binary mode of telnet daemon. binary_in and
33207c478bd9Sstevel@tonic-gate 		 * binary_out are 0 if not in binary mode.
33217c478bd9Sstevel@tonic-gate 		 */
33227c478bd9Sstevel@tonic-gate 		if (binary_in != myopts[TELOPT_BINARY] ||
33237c478bd9Sstevel@tonic-gate 		    binary_out != remopts[TELOPT_BINARY]) {
33247c478bd9Sstevel@tonic-gate 
33257c478bd9Sstevel@tonic-gate 			mode = 0;
33267c478bd9Sstevel@tonic-gate 			if (myopts[TELOPT_BINARY] != OPT_NO)
33277c478bd9Sstevel@tonic-gate 				mode |= TEL_BINARY_IN;
33287c478bd9Sstevel@tonic-gate 
33297c478bd9Sstevel@tonic-gate 			if (remopts[TELOPT_BINARY] != OPT_NO)
33307c478bd9Sstevel@tonic-gate 				mode |= TEL_BINARY_OUT;
33317c478bd9Sstevel@tonic-gate 
33327c478bd9Sstevel@tonic-gate 			telnetmod.ic_cmd = TEL_IOC_MODE;
33337c478bd9Sstevel@tonic-gate 			telnetmod.ic_timout = -1;
33347c478bd9Sstevel@tonic-gate 			telnetmod.ic_len = 1;
33357c478bd9Sstevel@tonic-gate 			telnetmod.ic_dp = &mode;
33367c478bd9Sstevel@tonic-gate 
33377c478bd9Sstevel@tonic-gate 			syslog(LOG_DEBUG, "TEL_IOC_MODE binary has changed\n");
33387c478bd9Sstevel@tonic-gate 
33397c478bd9Sstevel@tonic-gate 			if (ioctl(net, I_STR, &telnetmod) < 0)
33407c478bd9Sstevel@tonic-gate 				fatal(net, "ioctl TEL_IOC_MODE failed\n");
33417c478bd9Sstevel@tonic-gate 			binary_in = myopts[TELOPT_BINARY];
33427c478bd9Sstevel@tonic-gate 			binary_out = remopts[TELOPT_BINARY];
33437c478bd9Sstevel@tonic-gate 		}
33447c478bd9Sstevel@tonic-gate 		if (state == TS_DATA) {
33457c478bd9Sstevel@tonic-gate 			if ((nfrontp == nbackp) &&
33467c478bd9Sstevel@tonic-gate 				(pfrontp == pbackp)) {
33477c478bd9Sstevel@tonic-gate 				if (ioctl(net, I_NREAD, &nsize) < 0)
33487c478bd9Sstevel@tonic-gate 					fatalperror(net,
33497c478bd9Sstevel@tonic-gate 					    "ioctl I_NREAD failed\n", errno);
33507c478bd9Sstevel@tonic-gate 				if (nsize)
33517c478bd9Sstevel@tonic-gate 					drainstream(nsize);
33527c478bd9Sstevel@tonic-gate 
33537c478bd9Sstevel@tonic-gate 				/*
33547c478bd9Sstevel@tonic-gate 				 * make an ioctl to reinsert remaining data at
33557c478bd9Sstevel@tonic-gate 				 * streamhead. After this, ioctl reenables the
33567c478bd9Sstevel@tonic-gate 				 * telnet lower put queue. This queue was
33577c478bd9Sstevel@tonic-gate 				 * noenabled by telnet module after sending
33587c478bd9Sstevel@tonic-gate 				 * protocol/urgent data to telnetd.
33597c478bd9Sstevel@tonic-gate 				 */
33607c478bd9Sstevel@tonic-gate 
33617c478bd9Sstevel@tonic-gate 				telnetmod.ic_cmd = TEL_IOC_ENABLE;
33627c478bd9Sstevel@tonic-gate 				telnetmod.ic_timout = -1;
33637c478bd9Sstevel@tonic-gate 				if (ncc || nsize) {
33647c478bd9Sstevel@tonic-gate 					telnetmod.ic_len = ncc + nsize;
33657c478bd9Sstevel@tonic-gate 					telnetmod.ic_dp = netip;
33667c478bd9Sstevel@tonic-gate 				} else {
33677c478bd9Sstevel@tonic-gate 					telnetmod.ic_len = 0;
33687c478bd9Sstevel@tonic-gate 					telnetmod.ic_dp = NULL;
33697c478bd9Sstevel@tonic-gate 				}
33707c478bd9Sstevel@tonic-gate 				if (ioctl(net, I_STR, &telnetmod) < 0)
33717c478bd9Sstevel@tonic-gate 					fatal(net, "ioctl TEL_IOC_ENABLE \
33727c478bd9Sstevel@tonic-gate 						failed\n");
33737c478bd9Sstevel@tonic-gate 
33747c478bd9Sstevel@tonic-gate 				telmod_init_done = B_TRUE;
33757c478bd9Sstevel@tonic-gate 
33767c478bd9Sstevel@tonic-gate 				netip = netibuf;
33777c478bd9Sstevel@tonic-gate 				(void) memset(netibuf, 0, netibufsize);
33787c478bd9Sstevel@tonic-gate 
33797c478bd9Sstevel@tonic-gate 				ncc = 0;
33807c478bd9Sstevel@tonic-gate 			}
33817c478bd9Sstevel@tonic-gate 		} else {
33827c478bd9Sstevel@tonic-gate 			/*
33837c478bd9Sstevel@tonic-gate 			 * state not changed to TS_DATA and hence, more to read
33847c478bd9Sstevel@tonic-gate 			 * send ioctl to get one more message block.
33857c478bd9Sstevel@tonic-gate 			 */
33867c478bd9Sstevel@tonic-gate 			telnetmod.ic_cmd = TEL_IOC_GETBLK;
33877c478bd9Sstevel@tonic-gate 			telnetmod.ic_timout = -1;
33887c478bd9Sstevel@tonic-gate 			telnetmod.ic_len = 0;
33897c478bd9Sstevel@tonic-gate 			telnetmod.ic_dp = NULL;
33907c478bd9Sstevel@tonic-gate 
33917c478bd9Sstevel@tonic-gate 			if (ioctl(net, I_STR, &telnetmod) < 0)
33927c478bd9Sstevel@tonic-gate 				fatal(net, "ioctl TEL_IOC_GETBLK failed\n");
33937c478bd9Sstevel@tonic-gate 		}
33947c478bd9Sstevel@tonic-gate 
33957c478bd9Sstevel@tonic-gate 		if ((c = select(max(net, master) + 1, &ibits, &obits, &xbits,
33967c478bd9Sstevel@tonic-gate 		    (struct timeval *)0)) < 1) {
33977c478bd9Sstevel@tonic-gate 			if (c == -1) {
33987c478bd9Sstevel@tonic-gate 				if (errno == EINTR) {
33997c478bd9Sstevel@tonic-gate 					continue;
34007c478bd9Sstevel@tonic-gate 				}
34017c478bd9Sstevel@tonic-gate 			}
34027c478bd9Sstevel@tonic-gate 			(void) sleep(5);
34037c478bd9Sstevel@tonic-gate 			continue;
34047c478bd9Sstevel@tonic-gate 		}
34057c478bd9Sstevel@tonic-gate 
34067c478bd9Sstevel@tonic-gate 		/*
34077c478bd9Sstevel@tonic-gate 		 * Any urgent data?
34087c478bd9Sstevel@tonic-gate 		 */
34097c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &xbits)) {
34107c478bd9Sstevel@tonic-gate 			SYNCHing = 1;
34117c478bd9Sstevel@tonic-gate 		}
34127c478bd9Sstevel@tonic-gate 
34137c478bd9Sstevel@tonic-gate 		/*
34147c478bd9Sstevel@tonic-gate 		 * Something to read from the network...
34157c478bd9Sstevel@tonic-gate 		 */
34167c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &ibits)) {
34177c478bd9Sstevel@tonic-gate 		    ncc = read(net, netibuf, netibufsize);
34187c478bd9Sstevel@tonic-gate 		    if (ncc < 0 && errno == EWOULDBLOCK)
34197c478bd9Sstevel@tonic-gate 			ncc = 0;
34207c478bd9Sstevel@tonic-gate 		    else {
34217c478bd9Sstevel@tonic-gate 			if (ncc <= 0) {
34227c478bd9Sstevel@tonic-gate 			    break;
34237c478bd9Sstevel@tonic-gate 			}
34247c478bd9Sstevel@tonic-gate 			netip = netibuf;
34257c478bd9Sstevel@tonic-gate 		    }
34267c478bd9Sstevel@tonic-gate 		}
34277c478bd9Sstevel@tonic-gate 
34287c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)
34297c478bd9Sstevel@tonic-gate 			netflush();
34307c478bd9Sstevel@tonic-gate 		if (ncc > 0)
34317c478bd9Sstevel@tonic-gate 			telrcv();
34327c478bd9Sstevel@tonic-gate 		if (FD_ISSET(master, &obits) && (pfrontp - pbackp) > 0)
34337c478bd9Sstevel@tonic-gate 			ptyflush();
34347c478bd9Sstevel@tonic-gate 	}
34357c478bd9Sstevel@tonic-gate 	cleanup(0);
34367c478bd9Sstevel@tonic-gate }
34377c478bd9Sstevel@tonic-gate 
34387c478bd9Sstevel@tonic-gate static void
34397c478bd9Sstevel@tonic-gate telrcv(void)
34407c478bd9Sstevel@tonic-gate {
34417c478bd9Sstevel@tonic-gate 	int c;
34427c478bd9Sstevel@tonic-gate 
34437c478bd9Sstevel@tonic-gate 	while (ncc > 0) {
34447c478bd9Sstevel@tonic-gate 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
34457c478bd9Sstevel@tonic-gate 			return;
34467c478bd9Sstevel@tonic-gate 		c = *netip & 0377;
34477c478bd9Sstevel@tonic-gate 		/*
34487c478bd9Sstevel@tonic-gate 		 * Once we hit data, we want to transition back to
34497c478bd9Sstevel@tonic-gate 		 * in-kernel processing.  However, this code is shared
34507c478bd9Sstevel@tonic-gate 		 * by getterminaltype()/ttloop() which run before the
34517c478bd9Sstevel@tonic-gate 		 * in-kernel plumbing is available.  So if we are still
34527c478bd9Sstevel@tonic-gate 		 * processing the initial option negotiation, even TS_DATA
34537c478bd9Sstevel@tonic-gate 		 * must be processed here.
34547c478bd9Sstevel@tonic-gate 		 */
34557c478bd9Sstevel@tonic-gate 		if (c != IAC && state == TS_DATA && init_neg_done) {
34567c478bd9Sstevel@tonic-gate 			break;
34577c478bd9Sstevel@tonic-gate 		}
34587c478bd9Sstevel@tonic-gate 		netip++;
34597c478bd9Sstevel@tonic-gate 		ncc--;
34607c478bd9Sstevel@tonic-gate 		switch (state) {
34617c478bd9Sstevel@tonic-gate 
34627c478bd9Sstevel@tonic-gate 		case TS_CR:
34637c478bd9Sstevel@tonic-gate 			state = TS_DATA;
34647c478bd9Sstevel@tonic-gate 			/* Strip off \n or \0 after a \r */
34657c478bd9Sstevel@tonic-gate 			if ((c == 0) || (c == '\n')) {
34667c478bd9Sstevel@tonic-gate 				break;
34677c478bd9Sstevel@tonic-gate 			}
34687c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
34697c478bd9Sstevel@tonic-gate 
34707c478bd9Sstevel@tonic-gate 		case TS_DATA:
34717c478bd9Sstevel@tonic-gate 			if (c == IAC) {
34727c478bd9Sstevel@tonic-gate 				state = TS_IAC;
34737c478bd9Sstevel@tonic-gate 				break;
34747c478bd9Sstevel@tonic-gate 			}
34757c478bd9Sstevel@tonic-gate 			if (inter > 0)
34767c478bd9Sstevel@tonic-gate 				break;
34777c478bd9Sstevel@tonic-gate 			/*
34787c478bd9Sstevel@tonic-gate 			 * We map \r\n ==> \r, since
34797c478bd9Sstevel@tonic-gate 			 * We now map \r\n ==> \r for pragmatic reasons.
34807c478bd9Sstevel@tonic-gate 			 * Many client implementations send \r\n when
34817c478bd9Sstevel@tonic-gate 			 * the user hits the CarriageReturn key.
34827c478bd9Sstevel@tonic-gate 			 *
34837c478bd9Sstevel@tonic-gate 			 * We USED to map \r\n ==> \n, since \r\n says
34847c478bd9Sstevel@tonic-gate 			 * that we want to be in column 1 of the next
34857c478bd9Sstevel@tonic-gate 			 * line.
34867c478bd9Sstevel@tonic-gate 			 */
34877c478bd9Sstevel@tonic-gate 			if (c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
34887c478bd9Sstevel@tonic-gate 				state = TS_CR;
34897c478bd9Sstevel@tonic-gate 			}
34907c478bd9Sstevel@tonic-gate 			*pfrontp++ = c;
34917c478bd9Sstevel@tonic-gate 			break;
34927c478bd9Sstevel@tonic-gate 
34937c478bd9Sstevel@tonic-gate 		case TS_IAC:
34947c478bd9Sstevel@tonic-gate 			switch (c) {
34957c478bd9Sstevel@tonic-gate 
34967c478bd9Sstevel@tonic-gate 			/*
34977c478bd9Sstevel@tonic-gate 			 * Send the process on the pty side an
34987c478bd9Sstevel@tonic-gate 			 * interrupt.  Do this with a NULL or
34997c478bd9Sstevel@tonic-gate 			 * interrupt char; depending on the tty mode.
35007c478bd9Sstevel@tonic-gate 			 */
35017c478bd9Sstevel@tonic-gate 			case IP:
35027c478bd9Sstevel@tonic-gate 				interrupt();
35037c478bd9Sstevel@tonic-gate 				break;
35047c478bd9Sstevel@tonic-gate 
35057c478bd9Sstevel@tonic-gate 			case BREAK:
35067c478bd9Sstevel@tonic-gate 				sendbrk();
35077c478bd9Sstevel@tonic-gate 				break;
35087c478bd9Sstevel@tonic-gate 
35097c478bd9Sstevel@tonic-gate 			/*
35107c478bd9Sstevel@tonic-gate 			 * Are You There?
35117c478bd9Sstevel@tonic-gate 			 */
35127c478bd9Sstevel@tonic-gate 			case AYT:
35137c478bd9Sstevel@tonic-gate 				write_data_len("\r\n[Yes]\r\n", 9);
35147c478bd9Sstevel@tonic-gate 				break;
35157c478bd9Sstevel@tonic-gate 
35167c478bd9Sstevel@tonic-gate 			/*
35177c478bd9Sstevel@tonic-gate 			 * Abort Output
35187c478bd9Sstevel@tonic-gate 			 */
35197c478bd9Sstevel@tonic-gate 			case AO: {
35207c478bd9Sstevel@tonic-gate 					struct ltchars tmpltc;
35217c478bd9Sstevel@tonic-gate 
35227c478bd9Sstevel@tonic-gate 					ptyflush();	/* half-hearted */
35237c478bd9Sstevel@tonic-gate 					if (ioctl(pty, TIOCGLTC, &tmpltc) == -1)
35247c478bd9Sstevel@tonic-gate 						syslog(LOG_INFO,
35257c478bd9Sstevel@tonic-gate 						    "ioctl TIOCGLTC: %m\n");
35267c478bd9Sstevel@tonic-gate 					if (tmpltc.t_flushc != '\377') {
35277c478bd9Sstevel@tonic-gate 						*pfrontp++ = tmpltc.t_flushc;
35287c478bd9Sstevel@tonic-gate 					}
35297c478bd9Sstevel@tonic-gate 					netclear();	/* clear buffer back */
35307c478bd9Sstevel@tonic-gate 					write_data("%c%c", (uchar_t)IAC,
35317c478bd9Sstevel@tonic-gate 						(uchar_t)DM);
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 					neturg = nfrontp-1; /* off by one XXX */
35347c478bd9Sstevel@tonic-gate 					netflush();
35357c478bd9Sstevel@tonic-gate 					netflush(); /* XXX.sparker */
35367c478bd9Sstevel@tonic-gate 					break;
35377c478bd9Sstevel@tonic-gate 				}
35387c478bd9Sstevel@tonic-gate 
35397c478bd9Sstevel@tonic-gate 			/*
35407c478bd9Sstevel@tonic-gate 			 * Erase Character and
35417c478bd9Sstevel@tonic-gate 			 * Erase Line
35427c478bd9Sstevel@tonic-gate 			 */
35437c478bd9Sstevel@tonic-gate 			case EC:
35447c478bd9Sstevel@tonic-gate 			case EL: {
35457c478bd9Sstevel@tonic-gate 					struct sgttyb b;
35467c478bd9Sstevel@tonic-gate 					char ch;
35477c478bd9Sstevel@tonic-gate 
35487c478bd9Sstevel@tonic-gate 					ptyflush();	/* half-hearted */
35497c478bd9Sstevel@tonic-gate 					if (ioctl(pty, TIOCGETP, &b) == -1)
35507c478bd9Sstevel@tonic-gate 						syslog(LOG_INFO,
35517c478bd9Sstevel@tonic-gate 						    "ioctl TIOCGETP: %m\n");
35527c478bd9Sstevel@tonic-gate 					ch = (c == EC) ?
35537c478bd9Sstevel@tonic-gate 						b.sg_erase : b.sg_kill;
35547c478bd9Sstevel@tonic-gate 					if (ch != '\377') {
35557c478bd9Sstevel@tonic-gate 						*pfrontp++ = ch;
35567c478bd9Sstevel@tonic-gate 					}
35577c478bd9Sstevel@tonic-gate 					break;
35587c478bd9Sstevel@tonic-gate 				}
35597c478bd9Sstevel@tonic-gate 
35607c478bd9Sstevel@tonic-gate 			/*
35617c478bd9Sstevel@tonic-gate 			 * Check for urgent data...
35627c478bd9Sstevel@tonic-gate 			 */
35637c478bd9Sstevel@tonic-gate 			case DM:
35647c478bd9Sstevel@tonic-gate 				break;
35657c478bd9Sstevel@tonic-gate 
35667c478bd9Sstevel@tonic-gate 			/*
35677c478bd9Sstevel@tonic-gate 			 * Begin option subnegotiation...
35687c478bd9Sstevel@tonic-gate 			 */
35697c478bd9Sstevel@tonic-gate 			case SB:
35707c478bd9Sstevel@tonic-gate 				state = TS_SB;
35717c478bd9Sstevel@tonic-gate 				SB_CLEAR();
35727c478bd9Sstevel@tonic-gate 				continue;
35737c478bd9Sstevel@tonic-gate 
35747c478bd9Sstevel@tonic-gate 			case WILL:
35757c478bd9Sstevel@tonic-gate 				state = TS_WILL;
35767c478bd9Sstevel@tonic-gate 				continue;
35777c478bd9Sstevel@tonic-gate 
35787c478bd9Sstevel@tonic-gate 			case WONT:
35797c478bd9Sstevel@tonic-gate 				state = TS_WONT;
35807c478bd9Sstevel@tonic-gate 				continue;
35817c478bd9Sstevel@tonic-gate 
35827c478bd9Sstevel@tonic-gate 			case DO:
35837c478bd9Sstevel@tonic-gate 				state = TS_DO;
35847c478bd9Sstevel@tonic-gate 				continue;
35857c478bd9Sstevel@tonic-gate 
35867c478bd9Sstevel@tonic-gate 			case DONT:
35877c478bd9Sstevel@tonic-gate 				state = TS_DONT;
35887c478bd9Sstevel@tonic-gate 				continue;
35897c478bd9Sstevel@tonic-gate 
35907c478bd9Sstevel@tonic-gate 			case IAC:
35917c478bd9Sstevel@tonic-gate 				*pfrontp++ = c;
35927c478bd9Sstevel@tonic-gate 				break;
35937c478bd9Sstevel@tonic-gate 			}
35947c478bd9Sstevel@tonic-gate 			state = TS_DATA;
35957c478bd9Sstevel@tonic-gate 			break;
35967c478bd9Sstevel@tonic-gate 		case TS_SB:
35977c478bd9Sstevel@tonic-gate 			if (c == IAC) {
35987c478bd9Sstevel@tonic-gate 				state = TS_SE;
35997c478bd9Sstevel@tonic-gate 			} else {
36007c478bd9Sstevel@tonic-gate 				SB_ACCUM(c);
36017c478bd9Sstevel@tonic-gate 			}
36027c478bd9Sstevel@tonic-gate 			break;
36037c478bd9Sstevel@tonic-gate 		case TS_SE:
36047c478bd9Sstevel@tonic-gate 			if (c != SE) {
36057c478bd9Sstevel@tonic-gate 				if (c != IAC) {
36067c478bd9Sstevel@tonic-gate 					SB_ACCUM((uchar_t)IAC);
36077c478bd9Sstevel@tonic-gate 				}
36087c478bd9Sstevel@tonic-gate 				SB_ACCUM(c);
36097c478bd9Sstevel@tonic-gate 				state = TS_SB;
36107c478bd9Sstevel@tonic-gate 
36117c478bd9Sstevel@tonic-gate 			} else {
36127c478bd9Sstevel@tonic-gate 				SB_TERM();
36137c478bd9Sstevel@tonic-gate 				suboption();	/* handle sub-option */
36147c478bd9Sstevel@tonic-gate 				state = TS_DATA;
36157c478bd9Sstevel@tonic-gate 			}
36167c478bd9Sstevel@tonic-gate 			break;
36177c478bd9Sstevel@tonic-gate 
36187c478bd9Sstevel@tonic-gate 		case TS_WILL:
36197c478bd9Sstevel@tonic-gate 			if (remopts[c] != OPT_YES)
36207c478bd9Sstevel@tonic-gate 				willoption(c);
36217c478bd9Sstevel@tonic-gate 			state = TS_DATA;
36227c478bd9Sstevel@tonic-gate 			continue;
36237c478bd9Sstevel@tonic-gate 
36247c478bd9Sstevel@tonic-gate 		case TS_WONT:
36257c478bd9Sstevel@tonic-gate 			if (remopts[c] != OPT_NO)
36267c478bd9Sstevel@tonic-gate 				wontoption(c);
36277c478bd9Sstevel@tonic-gate 			state = TS_DATA;
36287c478bd9Sstevel@tonic-gate 			continue;
36297c478bd9Sstevel@tonic-gate 
36307c478bd9Sstevel@tonic-gate 		case TS_DO:
36317c478bd9Sstevel@tonic-gate 			if (myopts[c] != OPT_YES)
36327c478bd9Sstevel@tonic-gate 				dooption(c);
36337c478bd9Sstevel@tonic-gate 			state = TS_DATA;
36347c478bd9Sstevel@tonic-gate 			continue;
36357c478bd9Sstevel@tonic-gate 
36367c478bd9Sstevel@tonic-gate 		case TS_DONT:
36377c478bd9Sstevel@tonic-gate 			if (myopts[c] != OPT_NO) {
36387c478bd9Sstevel@tonic-gate 				dontoption(c);
36397c478bd9Sstevel@tonic-gate 			}
36407c478bd9Sstevel@tonic-gate 			state = TS_DATA;
36417c478bd9Sstevel@tonic-gate 			continue;
36427c478bd9Sstevel@tonic-gate 
36437c478bd9Sstevel@tonic-gate 		default:
36447c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
36457c478bd9Sstevel@tonic-gate 			(void) printf("telnetd: panic state=%d\n", state);
36467c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
36477c478bd9Sstevel@tonic-gate 		}
36487c478bd9Sstevel@tonic-gate 	}
36497c478bd9Sstevel@tonic-gate }
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate static void
36527c478bd9Sstevel@tonic-gate willoption(int option)
36537c478bd9Sstevel@tonic-gate {
36547c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
36557c478bd9Sstevel@tonic-gate 	boolean_t send_reply = B_TRUE;
36567c478bd9Sstevel@tonic-gate 
36577c478bd9Sstevel@tonic-gate 	switch (option) {
36587c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
36597c478bd9Sstevel@tonic-gate 		mode(O_RAW, 0);
36607c478bd9Sstevel@tonic-gate 		fmt = doopt;
36617c478bd9Sstevel@tonic-gate 		break;
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
36647c478bd9Sstevel@tonic-gate 		not42 = 0;		/* looks like a 4.2 system */
36657c478bd9Sstevel@tonic-gate 		/*
36667c478bd9Sstevel@tonic-gate 		 * Now, in a 4.2 system, to break them out of ECHOing
36677c478bd9Sstevel@tonic-gate 		 * (to the terminal) mode, we need to send a "WILL ECHO".
36687c478bd9Sstevel@tonic-gate 		 * Kludge upon kludge!
36697c478bd9Sstevel@tonic-gate 		 */
36707c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ECHO] == OPT_YES) {
36717c478bd9Sstevel@tonic-gate 			dooption(TELOPT_ECHO);
36727c478bd9Sstevel@tonic-gate 		}
36737c478bd9Sstevel@tonic-gate 		fmt = dont;
36747c478bd9Sstevel@tonic-gate 		break;
36757c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE:
36767c478bd9Sstevel@tonic-gate 		settimer(ttypeopt);
36777c478bd9Sstevel@tonic-gate 		goto common;
36787c478bd9Sstevel@tonic-gate 
36797c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS:
36807c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
36817c478bd9Sstevel@tonic-gate 		goto common;
36827c478bd9Sstevel@tonic-gate 
36837c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC:
36847c478bd9Sstevel@tonic-gate 		settimer(xdisplocopt);
36857c478bd9Sstevel@tonic-gate 		goto common;
36867c478bd9Sstevel@tonic-gate 
36877c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
36887c478bd9Sstevel@tonic-gate 		settimer(environopt);
36897c478bd9Sstevel@tonic-gate 		goto common;
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
36927c478bd9Sstevel@tonic-gate 		settimer(authopt);
36937c478bd9Sstevel@tonic-gate 		if (remopts[option] == OPT_NO ||
36947c478bd9Sstevel@tonic-gate 		    negotiate_auth_krb5 == 0)
36957c478bd9Sstevel@tonic-gate 			fmt = dont;
36967c478bd9Sstevel@tonic-gate 		else
36977c478bd9Sstevel@tonic-gate 			fmt = doopt;
36987c478bd9Sstevel@tonic-gate 		break;
36997c478bd9Sstevel@tonic-gate 
37007c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON:
37017c478bd9Sstevel@tonic-gate 		settimer(oenvironopt);
37027c478bd9Sstevel@tonic-gate 		goto common;
37037c478bd9Sstevel@tonic-gate common:
37047c478bd9Sstevel@tonic-gate 		if (remopts[option] == OPT_YES_BUT_ALWAYS_LOOK) {
37057c478bd9Sstevel@tonic-gate 			remopts[option] = OPT_YES;
37067c478bd9Sstevel@tonic-gate 			return;
37077c478bd9Sstevel@tonic-gate 		}
37087c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
37097c478bd9Sstevel@tonic-gate 	case TELOPT_SGA:
37107c478bd9Sstevel@tonic-gate 		fmt = doopt;
37117c478bd9Sstevel@tonic-gate 		break;
37127c478bd9Sstevel@tonic-gate 
37137c478bd9Sstevel@tonic-gate 	case TELOPT_TM:
37147c478bd9Sstevel@tonic-gate 		fmt = dont;
37157c478bd9Sstevel@tonic-gate 		break;
37167c478bd9Sstevel@tonic-gate 
37177c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
37187c478bd9Sstevel@tonic-gate 		settimer(encropt); /* got response to do/dont */
37197c478bd9Sstevel@tonic-gate 		if (enc_debug)
37207c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
37217c478bd9Sstevel@tonic-gate 				    "RCVD IAC WILL TELOPT_ENCRYPT\n");
37227c478bd9Sstevel@tonic-gate 		if (krb5_privacy_allowed()) {
37237c478bd9Sstevel@tonic-gate 			fmt = doopt;
37247c478bd9Sstevel@tonic-gate 			if (sent_do_encrypt)
37257c478bd9Sstevel@tonic-gate 				send_reply = B_FALSE;
37267c478bd9Sstevel@tonic-gate 			else
37277c478bd9Sstevel@tonic-gate 				sent_do_encrypt = B_TRUE;
37287c478bd9Sstevel@tonic-gate 		} else {
37297c478bd9Sstevel@tonic-gate 			fmt = dont;
37307c478bd9Sstevel@tonic-gate 		}
37317c478bd9Sstevel@tonic-gate 		break;
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 	default:
37347c478bd9Sstevel@tonic-gate 		fmt = dont;
37357c478bd9Sstevel@tonic-gate 		break;
37367c478bd9Sstevel@tonic-gate 	}
37377c478bd9Sstevel@tonic-gate 	if (fmt == doopt) {
37387c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_YES;
37397c478bd9Sstevel@tonic-gate 	} else {
37407c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_NO;
37417c478bd9Sstevel@tonic-gate 	}
37427c478bd9Sstevel@tonic-gate 	if (send_reply) {
37437c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
37447c478bd9Sstevel@tonic-gate 		netflush();
37457c478bd9Sstevel@tonic-gate 	}
37467c478bd9Sstevel@tonic-gate }
37477c478bd9Sstevel@tonic-gate 
37487c478bd9Sstevel@tonic-gate static void
37497c478bd9Sstevel@tonic-gate wontoption(int option)
37507c478bd9Sstevel@tonic-gate {
37517c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
37527c478bd9Sstevel@tonic-gate 	int send_reply = 1;
37537c478bd9Sstevel@tonic-gate 
37547c478bd9Sstevel@tonic-gate 	switch (option) {
37557c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
37567c478bd9Sstevel@tonic-gate 		not42 = 1;		/* doesn't seem to be a 4.2 system */
37577c478bd9Sstevel@tonic-gate 		break;
37587c478bd9Sstevel@tonic-gate 
37597c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
37607c478bd9Sstevel@tonic-gate 		mode(0, O_RAW);
37617c478bd9Sstevel@tonic-gate 		break;
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE:
37647c478bd9Sstevel@tonic-gate 		settimer(ttypeopt);
37657c478bd9Sstevel@tonic-gate 		break;
37667c478bd9Sstevel@tonic-gate 
37677c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS:
37687c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
37697c478bd9Sstevel@tonic-gate 		break;
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC:
37727c478bd9Sstevel@tonic-gate 		settimer(xdisplocopt);
37737c478bd9Sstevel@tonic-gate 		break;
37747c478bd9Sstevel@tonic-gate 
37757c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
37767c478bd9Sstevel@tonic-gate 		settimer(environopt);
37777c478bd9Sstevel@tonic-gate 		break;
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON:
37807c478bd9Sstevel@tonic-gate 		settimer(oenvironopt);
37817c478bd9Sstevel@tonic-gate 		break;
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
37847c478bd9Sstevel@tonic-gate 		settimer(authopt);
37857c478bd9Sstevel@tonic-gate 		auth_finished(0, AUTH_REJECT);
37867c478bd9Sstevel@tonic-gate 		if (auth_debug)
37877c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
37887c478bd9Sstevel@tonic-gate 				    "RCVD WONT TELOPT_AUTHENTICATE\n");
37897c478bd9Sstevel@tonic-gate 
37907c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_NO;
37917c478bd9Sstevel@tonic-gate 		send_reply = 0;
37927c478bd9Sstevel@tonic-gate 		break;
37937c478bd9Sstevel@tonic-gate 
37947c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
37957c478bd9Sstevel@tonic-gate 		if (enc_debug)
37967c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
37977c478bd9Sstevel@tonic-gate 				    "RCVD IAC WONT TELOPT_ENCRYPT\n");
37987c478bd9Sstevel@tonic-gate 		settimer(encropt); /* got response to will/wont */
37997c478bd9Sstevel@tonic-gate 		/*
38007c478bd9Sstevel@tonic-gate 		 * Remote side cannot send encryption. No reply necessary
38017c478bd9Sstevel@tonic-gate 		 * Treat this as if "IAC SB ENCRYPT END IAC SE" were
38027c478bd9Sstevel@tonic-gate 		 * received (RFC 2946) and disable crypto.
38037c478bd9Sstevel@tonic-gate 		 */
38047c478bd9Sstevel@tonic-gate 		encrypt_end(TELNET_DIR_DECRYPT);
38057c478bd9Sstevel@tonic-gate 		send_reply = 0;
38067c478bd9Sstevel@tonic-gate 		break;
38077c478bd9Sstevel@tonic-gate 	}
38087c478bd9Sstevel@tonic-gate 
38097c478bd9Sstevel@tonic-gate 	fmt = dont;
38107c478bd9Sstevel@tonic-gate 	remopts[option] = OPT_NO;
38117c478bd9Sstevel@tonic-gate 	if (send_reply) {
38127c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
38137c478bd9Sstevel@tonic-gate 	}
38147c478bd9Sstevel@tonic-gate }
38157c478bd9Sstevel@tonic-gate 
38167c478bd9Sstevel@tonic-gate /*
38177c478bd9Sstevel@tonic-gate  * We received an "IAC DO ..." message from the client, change our state
38187c478bd9Sstevel@tonic-gate  * to OPT_YES.
38197c478bd9Sstevel@tonic-gate  */
38207c478bd9Sstevel@tonic-gate static void
38217c478bd9Sstevel@tonic-gate dooption(int option)
38227c478bd9Sstevel@tonic-gate {
38237c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
38247c478bd9Sstevel@tonic-gate 	boolean_t send_reply = B_TRUE;
38257c478bd9Sstevel@tonic-gate 
38267c478bd9Sstevel@tonic-gate 	switch (option) {
38277c478bd9Sstevel@tonic-gate 
38287c478bd9Sstevel@tonic-gate 	case TELOPT_TM:
38297c478bd9Sstevel@tonic-gate 		fmt = wont;
38307c478bd9Sstevel@tonic-gate 		break;
38317c478bd9Sstevel@tonic-gate 
38327c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
38337c478bd9Sstevel@tonic-gate 		mode(O_ECHO|O_CRMOD, 0);
38347c478bd9Sstevel@tonic-gate 		fmt = will;
38357c478bd9Sstevel@tonic-gate 		break;
38367c478bd9Sstevel@tonic-gate 
38377c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
38387c478bd9Sstevel@tonic-gate 		mode(O_RAW, 0);
38397c478bd9Sstevel@tonic-gate 		fmt = will;
38407c478bd9Sstevel@tonic-gate 		break;
38417c478bd9Sstevel@tonic-gate 
38427c478bd9Sstevel@tonic-gate 	case TELOPT_SGA:
38437c478bd9Sstevel@tonic-gate 		fmt = will;
38447c478bd9Sstevel@tonic-gate 		break;
38457c478bd9Sstevel@tonic-gate 
38467c478bd9Sstevel@tonic-gate 	case TELOPT_LOGOUT:
38477c478bd9Sstevel@tonic-gate 		/*
38487c478bd9Sstevel@tonic-gate 		 * Options don't get much easier.  Acknowledge the option,
38497c478bd9Sstevel@tonic-gate 		 * and then clean up and exit.
38507c478bd9Sstevel@tonic-gate 		 */
38517c478bd9Sstevel@tonic-gate 		write_data((const char *)will, option);
38527c478bd9Sstevel@tonic-gate 		netflush();
38537c478bd9Sstevel@tonic-gate 		cleanup(0);
38547c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
38557c478bd9Sstevel@tonic-gate 
38567c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
38577c478bd9Sstevel@tonic-gate 		if (enc_debug)
38587c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD DO TELOPT_ENCRYPT\n");
38597c478bd9Sstevel@tonic-gate 		settimer(encropt);
38607c478bd9Sstevel@tonic-gate 		/*
38617c478bd9Sstevel@tonic-gate 		 * We received a "DO".  This indicates that the other side
38627c478bd9Sstevel@tonic-gate 		 * wants us to encrypt our data (pending negotiatoin).
38637c478bd9Sstevel@tonic-gate 		 * reply with "IAC WILL ENCRYPT" if we are able to send
38647c478bd9Sstevel@tonic-gate 		 * encrypted data.
38657c478bd9Sstevel@tonic-gate 		 */
38667c478bd9Sstevel@tonic-gate 		if (krb5_privacy_allowed() && negotiate_encrypt) {
38677c478bd9Sstevel@tonic-gate 			fmt = will;
38687c478bd9Sstevel@tonic-gate 			if (sent_will_encrypt)
38697c478bd9Sstevel@tonic-gate 				send_reply = B_FALSE;
38707c478bd9Sstevel@tonic-gate 			else
38717c478bd9Sstevel@tonic-gate 				sent_will_encrypt = B_TRUE;
38727c478bd9Sstevel@tonic-gate 			/* return if we already sent "WILL ENCRYPT" */
38737c478bd9Sstevel@tonic-gate 			if (myopts[option] == OPT_YES)
38747c478bd9Sstevel@tonic-gate 				return;
38757c478bd9Sstevel@tonic-gate 		} else {
38767c478bd9Sstevel@tonic-gate 			fmt = wont;
38777c478bd9Sstevel@tonic-gate 		}
38787c478bd9Sstevel@tonic-gate 		break;
38797c478bd9Sstevel@tonic-gate 
38807c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
38817c478bd9Sstevel@tonic-gate 		if (auth_debug) {
38827c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
38837c478bd9Sstevel@tonic-gate 				    "RCVD DO TELOPT_AUTHENTICATION\n");
38847c478bd9Sstevel@tonic-gate 		}
38857c478bd9Sstevel@tonic-gate 		/*
38867c478bd9Sstevel@tonic-gate 		 * RFC 2941 - only the server can send
38877c478bd9Sstevel@tonic-gate 		 * "DO TELOPT_AUTHENTICATION".
38887c478bd9Sstevel@tonic-gate 		 * if a server receives this, it must respond with WONT...
38897c478bd9Sstevel@tonic-gate 		 */
38907c478bd9Sstevel@tonic-gate 		fmt = wont;
38917c478bd9Sstevel@tonic-gate 		break;
38927c478bd9Sstevel@tonic-gate 
38937c478bd9Sstevel@tonic-gate 	default:
38947c478bd9Sstevel@tonic-gate 		fmt = wont;
38957c478bd9Sstevel@tonic-gate 		break;
38967c478bd9Sstevel@tonic-gate 	}
38977c478bd9Sstevel@tonic-gate 	if (fmt == will) {
38987c478bd9Sstevel@tonic-gate 		myopts[option] = OPT_YES;
38997c478bd9Sstevel@tonic-gate 	} else {
39007c478bd9Sstevel@tonic-gate 		myopts[option] = OPT_NO;
39017c478bd9Sstevel@tonic-gate 	}
39027c478bd9Sstevel@tonic-gate 	if (send_reply) {
39037c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
39047c478bd9Sstevel@tonic-gate 		netflush();
39057c478bd9Sstevel@tonic-gate 	}
39067c478bd9Sstevel@tonic-gate }
39077c478bd9Sstevel@tonic-gate 
39087c478bd9Sstevel@tonic-gate /*
39097c478bd9Sstevel@tonic-gate  * We received an "IAC DONT ..." message from client.
39107c478bd9Sstevel@tonic-gate  * Client does not agree with the option so act accordingly.
39117c478bd9Sstevel@tonic-gate  */
39127c478bd9Sstevel@tonic-gate static void
39137c478bd9Sstevel@tonic-gate dontoption(int option)
39147c478bd9Sstevel@tonic-gate {
39157c478bd9Sstevel@tonic-gate 	int send_reply = 1;
39167c478bd9Sstevel@tonic-gate 	switch (option) {
39177c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
39187c478bd9Sstevel@tonic-gate 		/*
39197c478bd9Sstevel@tonic-gate 		 * we should stop echoing, since the client side will be doing
39207c478bd9Sstevel@tonic-gate 		 * it, but keep mapping CR since CR-LF will be mapped to it.
39217c478bd9Sstevel@tonic-gate 		 */
39227c478bd9Sstevel@tonic-gate 		mode(0, O_ECHO);
39237c478bd9Sstevel@tonic-gate 		break;
39247c478bd9Sstevel@tonic-gate 
39257c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
39267c478bd9Sstevel@tonic-gate 		if (enc_debug)
39277c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD IAC DONT ENCRYPT\n");
39287c478bd9Sstevel@tonic-gate 		settimer(encropt);
39297c478bd9Sstevel@tonic-gate 		/*
39307c478bd9Sstevel@tonic-gate 		 * Remote side cannot receive any encrypted data,
39317c478bd9Sstevel@tonic-gate 		 * so dont send any.  No reply necessary.
39327c478bd9Sstevel@tonic-gate 		 */
39337c478bd9Sstevel@tonic-gate 		send_reply = 0;
39347c478bd9Sstevel@tonic-gate 		break;
39357c478bd9Sstevel@tonic-gate 
39367c478bd9Sstevel@tonic-gate 	default:
39377c478bd9Sstevel@tonic-gate 		break;
39387c478bd9Sstevel@tonic-gate 	}
39397c478bd9Sstevel@tonic-gate 
39407c478bd9Sstevel@tonic-gate 	myopts[option] = OPT_NO;
39417c478bd9Sstevel@tonic-gate 
39427c478bd9Sstevel@tonic-gate 	if (send_reply) {
39437c478bd9Sstevel@tonic-gate 		write_data((const char *)wont, option);
39447c478bd9Sstevel@tonic-gate 	}
39457c478bd9Sstevel@tonic-gate }
39467c478bd9Sstevel@tonic-gate 
39477c478bd9Sstevel@tonic-gate /*
39487c478bd9Sstevel@tonic-gate  * suboption()
39497c478bd9Sstevel@tonic-gate  *
39507c478bd9Sstevel@tonic-gate  *	Look at the sub-option buffer, and try to be helpful to the other
39517c478bd9Sstevel@tonic-gate  * side.
39527c478bd9Sstevel@tonic-gate  *
39537c478bd9Sstevel@tonic-gate  */
39547c478bd9Sstevel@tonic-gate static void
39557c478bd9Sstevel@tonic-gate suboption(void)
39567c478bd9Sstevel@tonic-gate {
39577c478bd9Sstevel@tonic-gate 	int subchar;
39587c478bd9Sstevel@tonic-gate 
39597c478bd9Sstevel@tonic-gate 	switch (subchar = SB_GET()) {
39607c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE: {		/* Yaaaay! */
39617c478bd9Sstevel@tonic-gate 		static char terminalname[5+41] = "TERM=";
39627c478bd9Sstevel@tonic-gate 
39637c478bd9Sstevel@tonic-gate 		settimer(ttypesubopt);
39647c478bd9Sstevel@tonic-gate 
39657c478bd9Sstevel@tonic-gate 		if (SB_GET() != TELQUAL_IS) {
39667c478bd9Sstevel@tonic-gate 			return;	/* ??? XXX but, this is the most robust */
39677c478bd9Sstevel@tonic-gate 		}
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 		terminaltype = terminalname+strlen(terminalname);
39707c478bd9Sstevel@tonic-gate 
39717c478bd9Sstevel@tonic-gate 		while (terminaltype < (terminalname + sizeof (terminalname) -
39727c478bd9Sstevel@tonic-gate 		    1) && !SB_EOF()) {
39737c478bd9Sstevel@tonic-gate 			int c;
39747c478bd9Sstevel@tonic-gate 
39757c478bd9Sstevel@tonic-gate 			c = SB_GET();
39767c478bd9Sstevel@tonic-gate 			if (isupper(c)) {
39777c478bd9Sstevel@tonic-gate 				c = tolower(c);
39787c478bd9Sstevel@tonic-gate 			}
39797c478bd9Sstevel@tonic-gate 			*terminaltype++ = c;    /* accumulate name */
39807c478bd9Sstevel@tonic-gate 		}
39817c478bd9Sstevel@tonic-gate 		*terminaltype = 0;
39827c478bd9Sstevel@tonic-gate 		terminaltype = terminalname;
39837c478bd9Sstevel@tonic-gate 		break;
39847c478bd9Sstevel@tonic-gate 	}
39857c478bd9Sstevel@tonic-gate 
39867c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS: {
39877c478bd9Sstevel@tonic-gate 		struct winsize ws;
39887c478bd9Sstevel@tonic-gate 
39897c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
39907c478bd9Sstevel@tonic-gate 			return;
39917c478bd9Sstevel@tonic-gate 		}
39927c478bd9Sstevel@tonic-gate 		ws.ws_col = SB_GET() << 8;
39937c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
39947c478bd9Sstevel@tonic-gate 			return;
39957c478bd9Sstevel@tonic-gate 		}
39967c478bd9Sstevel@tonic-gate 		ws.ws_col |= SB_GET();
39977c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
39987c478bd9Sstevel@tonic-gate 			return;
39997c478bd9Sstevel@tonic-gate 		}
40007c478bd9Sstevel@tonic-gate 		ws.ws_row = SB_GET() << 8;
40017c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
40027c478bd9Sstevel@tonic-gate 			return;
40037c478bd9Sstevel@tonic-gate 		}
40047c478bd9Sstevel@tonic-gate 		ws.ws_row |= SB_GET();
40057c478bd9Sstevel@tonic-gate 		ws.ws_xpixel = 0; ws.ws_ypixel = 0;
40067c478bd9Sstevel@tonic-gate 		(void) ioctl(pty, TIOCSWINSZ, &ws);
40077c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
40087c478bd9Sstevel@tonic-gate 		break;
40097c478bd9Sstevel@tonic-gate 	}
40107c478bd9Sstevel@tonic-gate 
40117c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC: {
40127c478bd9Sstevel@tonic-gate 		if (SB_EOF() || SB_GET() != TELQUAL_IS) {
40137c478bd9Sstevel@tonic-gate 			return;
40147c478bd9Sstevel@tonic-gate 		}
40157c478bd9Sstevel@tonic-gate 		settimer(xdisplocsubopt);
40167c478bd9Sstevel@tonic-gate 		subpointer[SB_LEN()] = '\0';
40177c478bd9Sstevel@tonic-gate 		if ((new_env("DISPLAY", subpointer)) == 1)
40187c478bd9Sstevel@tonic-gate 			perror("malloc");
40197c478bd9Sstevel@tonic-gate 		break;
40207c478bd9Sstevel@tonic-gate 	}
40217c478bd9Sstevel@tonic-gate 
40227c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
40237c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON: {
40247c478bd9Sstevel@tonic-gate 		int c;
40257c478bd9Sstevel@tonic-gate 		char *cp, *varp, *valp;
40267c478bd9Sstevel@tonic-gate 
40277c478bd9Sstevel@tonic-gate 		if (SB_EOF())
40287c478bd9Sstevel@tonic-gate 			return;
40297c478bd9Sstevel@tonic-gate 		c = SB_GET();
40307c478bd9Sstevel@tonic-gate 		if (c == TELQUAL_IS) {
40317c478bd9Sstevel@tonic-gate 			if (subchar == TELOPT_OLD_ENVIRON)
40327c478bd9Sstevel@tonic-gate 				settimer(oenvironsubopt);
40337c478bd9Sstevel@tonic-gate 			else
40347c478bd9Sstevel@tonic-gate 				settimer(environsubopt);
40357c478bd9Sstevel@tonic-gate 		} else if (c != TELQUAL_INFO) {
40367c478bd9Sstevel@tonic-gate 			return;
40377c478bd9Sstevel@tonic-gate 		}
40387c478bd9Sstevel@tonic-gate 
40397c478bd9Sstevel@tonic-gate 		if (subchar == TELOPT_NEW_ENVIRON) {
40407c478bd9Sstevel@tonic-gate 		    while (!SB_EOF()) {
40417c478bd9Sstevel@tonic-gate 			c = SB_GET();
40427c478bd9Sstevel@tonic-gate 			if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
40437c478bd9Sstevel@tonic-gate 				break;
40447c478bd9Sstevel@tonic-gate 		    }
40457c478bd9Sstevel@tonic-gate 		} else
40467c478bd9Sstevel@tonic-gate 		{
40477c478bd9Sstevel@tonic-gate 			while (!SB_EOF()) {
40487c478bd9Sstevel@tonic-gate 				c = SB_GET();
40497c478bd9Sstevel@tonic-gate 				if ((c == env_ovar) || (c == ENV_USERVAR))
40507c478bd9Sstevel@tonic-gate 					break;
40517c478bd9Sstevel@tonic-gate 			}
40527c478bd9Sstevel@tonic-gate 		}
40537c478bd9Sstevel@tonic-gate 
40547c478bd9Sstevel@tonic-gate 		if (SB_EOF())
40557c478bd9Sstevel@tonic-gate 			return;
40567c478bd9Sstevel@tonic-gate 
40577c478bd9Sstevel@tonic-gate 		cp = varp = (char *)subpointer;
40587c478bd9Sstevel@tonic-gate 		valp = 0;
40597c478bd9Sstevel@tonic-gate 
40607c478bd9Sstevel@tonic-gate 		while (!SB_EOF()) {
40617c478bd9Sstevel@tonic-gate 			c = SB_GET();
40627c478bd9Sstevel@tonic-gate 			if (subchar == TELOPT_OLD_ENVIRON) {
40637c478bd9Sstevel@tonic-gate 				if (c == env_ovar)
40647c478bd9Sstevel@tonic-gate 					c = NEW_ENV_VAR;
40657c478bd9Sstevel@tonic-gate 				else if (c == env_ovalue)
40667c478bd9Sstevel@tonic-gate 					c = NEW_ENV_VALUE;
40677c478bd9Sstevel@tonic-gate 			}
40687c478bd9Sstevel@tonic-gate 			switch (c) {
40697c478bd9Sstevel@tonic-gate 
40707c478bd9Sstevel@tonic-gate 			case NEW_ENV_VALUE:
40717c478bd9Sstevel@tonic-gate 				*cp = '\0';
40727c478bd9Sstevel@tonic-gate 				cp = valp = (char *)subpointer;
40737c478bd9Sstevel@tonic-gate 				break;
40747c478bd9Sstevel@tonic-gate 
40757c478bd9Sstevel@tonic-gate 			case NEW_ENV_VAR:
40767c478bd9Sstevel@tonic-gate 			case ENV_USERVAR:
40777c478bd9Sstevel@tonic-gate 				*cp = '\0';
40787c478bd9Sstevel@tonic-gate 				if (valp) {
40797c478bd9Sstevel@tonic-gate 					if ((new_env(varp, valp)) == 1) {
40807c478bd9Sstevel@tonic-gate 						perror("malloc");
40817c478bd9Sstevel@tonic-gate 					}
40827c478bd9Sstevel@tonic-gate 				} else {
40837c478bd9Sstevel@tonic-gate 					(void) del_env(varp);
40847c478bd9Sstevel@tonic-gate 				}
40857c478bd9Sstevel@tonic-gate 				cp = varp = (char *)subpointer;
40867c478bd9Sstevel@tonic-gate 				valp = 0;
40877c478bd9Sstevel@tonic-gate 				break;
40887c478bd9Sstevel@tonic-gate 
40897c478bd9Sstevel@tonic-gate 			case ENV_ESC:
40907c478bd9Sstevel@tonic-gate 				if (SB_EOF())
40917c478bd9Sstevel@tonic-gate 					break;
40927c478bd9Sstevel@tonic-gate 				c = SB_GET();
40937c478bd9Sstevel@tonic-gate 				/* FALL THROUGH */
40947c478bd9Sstevel@tonic-gate 			default:
40957c478bd9Sstevel@tonic-gate 				*cp++ = c;
40967c478bd9Sstevel@tonic-gate 				break;
40977c478bd9Sstevel@tonic-gate 			}
40987c478bd9Sstevel@tonic-gate 		}
40997c478bd9Sstevel@tonic-gate 		*cp = '\0';
41007c478bd9Sstevel@tonic-gate 		if (valp) {
41017c478bd9Sstevel@tonic-gate 			if ((new_env(varp, valp)) == 1) {
41027c478bd9Sstevel@tonic-gate 				perror("malloc");
41037c478bd9Sstevel@tonic-gate 			}
41047c478bd9Sstevel@tonic-gate 		} else {
41057c478bd9Sstevel@tonic-gate 			(void) del_env(varp);
41067c478bd9Sstevel@tonic-gate 		}
41077c478bd9Sstevel@tonic-gate 		break;
41087c478bd9Sstevel@tonic-gate 	}  /* end of case TELOPT_NEW_ENVIRON */
41097c478bd9Sstevel@tonic-gate 
41107c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
41117c478bd9Sstevel@tonic-gate 		if (SB_EOF())
41127c478bd9Sstevel@tonic-gate 			break;
41137c478bd9Sstevel@tonic-gate 		switch (SB_GET()) {
41147c478bd9Sstevel@tonic-gate 		case TELQUAL_SEND:
41157c478bd9Sstevel@tonic-gate 		case TELQUAL_REPLY:
41167c478bd9Sstevel@tonic-gate 			/*
41177c478bd9Sstevel@tonic-gate 			 * These are sent server only and cannot be sent by the
41187c478bd9Sstevel@tonic-gate 			 * client.
41197c478bd9Sstevel@tonic-gate 			 */
41207c478bd9Sstevel@tonic-gate 			break;
41217c478bd9Sstevel@tonic-gate 		case TELQUAL_IS:
41227c478bd9Sstevel@tonic-gate 			if (auth_debug)
41237c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
41247c478bd9Sstevel@tonic-gate 					    "RCVD AUTHENTICATION IS "
41257c478bd9Sstevel@tonic-gate 					    "(%d bytes)\n",
41267c478bd9Sstevel@tonic-gate 					    SB_LEN());
41277c478bd9Sstevel@tonic-gate 			if (!auth_negotiated)
41287c478bd9Sstevel@tonic-gate 				auth_is((uchar_t *)subpointer, SB_LEN());
41297c478bd9Sstevel@tonic-gate 			break;
41307c478bd9Sstevel@tonic-gate 		case TELQUAL_NAME:
41317c478bd9Sstevel@tonic-gate 			if (auth_debug)
41327c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
41337c478bd9Sstevel@tonic-gate 					    "RCVD AUTHENTICATION NAME "
41347c478bd9Sstevel@tonic-gate 					    "(%d bytes)\n",
41357c478bd9Sstevel@tonic-gate 					    SB_LEN());
41367c478bd9Sstevel@tonic-gate 			if (!auth_negotiated)
41377c478bd9Sstevel@tonic-gate 				auth_name((uchar_t *)subpointer, SB_LEN());
41387c478bd9Sstevel@tonic-gate 			break;
41397c478bd9Sstevel@tonic-gate 		}
41407c478bd9Sstevel@tonic-gate 		break;
41417c478bd9Sstevel@tonic-gate 
41427c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT: {
41437c478bd9Sstevel@tonic-gate 		int c;
41447c478bd9Sstevel@tonic-gate 		if (SB_EOF())
41457c478bd9Sstevel@tonic-gate 			break;
41467c478bd9Sstevel@tonic-gate 		c = SB_GET();
41477c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
41487c478bd9Sstevel@tonic-gate 		if (enc_debug)
41497c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD ENCRYPT %s\n",
41507c478bd9Sstevel@tonic-gate 				    ENCRYPT_NAME(c));
41517c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
41527c478bd9Sstevel@tonic-gate 		switch (c) {
41537c478bd9Sstevel@tonic-gate 		case ENCRYPT_SUPPORT:
41547c478bd9Sstevel@tonic-gate 			encrypt_support(subpointer, SB_LEN());
41557c478bd9Sstevel@tonic-gate 			break;
41567c478bd9Sstevel@tonic-gate 		case ENCRYPT_IS:
41577c478bd9Sstevel@tonic-gate 			encrypt_is((uchar_t *)subpointer, SB_LEN());
41587c478bd9Sstevel@tonic-gate 			break;
41597c478bd9Sstevel@tonic-gate 		case ENCRYPT_REPLY:
41607c478bd9Sstevel@tonic-gate 			(void) encrypt_reply(subpointer, SB_LEN());
41617c478bd9Sstevel@tonic-gate 			break;
41627c478bd9Sstevel@tonic-gate 		case ENCRYPT_START:
41637c478bd9Sstevel@tonic-gate 			encrypt_start();
41647c478bd9Sstevel@tonic-gate 			break;
41657c478bd9Sstevel@tonic-gate 		case ENCRYPT_END:
41667c478bd9Sstevel@tonic-gate 			encrypt_end(TELNET_DIR_DECRYPT);
41677c478bd9Sstevel@tonic-gate 			break;
41687c478bd9Sstevel@tonic-gate 		case ENCRYPT_REQSTART:
41697c478bd9Sstevel@tonic-gate 			encrypt_request_start();
41707c478bd9Sstevel@tonic-gate 			break;
41717c478bd9Sstevel@tonic-gate 		case ENCRYPT_REQEND:
41727c478bd9Sstevel@tonic-gate 			/*
41737c478bd9Sstevel@tonic-gate 			 * We can always send an REQEND so that we cannot
41747c478bd9Sstevel@tonic-gate 			 * get stuck encrypting.  We should only get this
41757c478bd9Sstevel@tonic-gate 			 * if we have been able to get in the correct mode
41767c478bd9Sstevel@tonic-gate 			 * anyhow.
41777c478bd9Sstevel@tonic-gate 			 */
41787c478bd9Sstevel@tonic-gate 			encrypt_request_end();
41797c478bd9Sstevel@tonic-gate 			break;
41807c478bd9Sstevel@tonic-gate 		case ENCRYPT_ENC_KEYID:
41817c478bd9Sstevel@tonic-gate 			encrypt_enc_keyid(subpointer, SB_LEN());
41827c478bd9Sstevel@tonic-gate 			break;
41837c478bd9Sstevel@tonic-gate 		case ENCRYPT_DEC_KEYID:
41847c478bd9Sstevel@tonic-gate 			encrypt_dec_keyid(subpointer, SB_LEN());
41857c478bd9Sstevel@tonic-gate 			break;
41867c478bd9Sstevel@tonic-gate 		default:
41877c478bd9Sstevel@tonic-gate 			break;
41887c478bd9Sstevel@tonic-gate 		}
41897c478bd9Sstevel@tonic-gate 	}
41907c478bd9Sstevel@tonic-gate 	break;
41917c478bd9Sstevel@tonic-gate 
41927c478bd9Sstevel@tonic-gate 	default:
41937c478bd9Sstevel@tonic-gate 		break;
41947c478bd9Sstevel@tonic-gate 	}
41957c478bd9Sstevel@tonic-gate }
41967c478bd9Sstevel@tonic-gate 
41977c478bd9Sstevel@tonic-gate static void
41987c478bd9Sstevel@tonic-gate mode(int on, int off)
41997c478bd9Sstevel@tonic-gate {
42007c478bd9Sstevel@tonic-gate 	struct termios  tios;
42017c478bd9Sstevel@tonic-gate 
42027c478bd9Sstevel@tonic-gate 	ptyflush();
42037c478bd9Sstevel@tonic-gate 	if (tcgetattr(pty, &tios) < 0)
42047c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "tcgetattr: %m\n");
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate 	if (on & O_RAW) {
42077c478bd9Sstevel@tonic-gate 		tios.c_cflag |= CS8;
42087c478bd9Sstevel@tonic-gate 		tios.c_iflag &= ~IUCLC;
42097c478bd9Sstevel@tonic-gate 		tios.c_lflag &= ~(XCASE|IEXTEN);
42107c478bd9Sstevel@tonic-gate 	}
42117c478bd9Sstevel@tonic-gate 	if (off & O_RAW) {
42127c478bd9Sstevel@tonic-gate 		if ((tios.c_cflag & PARENB) != 0)
42137c478bd9Sstevel@tonic-gate 			tios.c_cflag &= ~CS8;
42147c478bd9Sstevel@tonic-gate 		tios.c_lflag |= IEXTEN;
42157c478bd9Sstevel@tonic-gate 	}
42167c478bd9Sstevel@tonic-gate 
42177c478bd9Sstevel@tonic-gate 	if (on & O_ECHO)
42187c478bd9Sstevel@tonic-gate 		tios.c_lflag |= ECHO;
42197c478bd9Sstevel@tonic-gate 	if (off & O_ECHO)
42207c478bd9Sstevel@tonic-gate 		tios.c_lflag &= ~ECHO;
42217c478bd9Sstevel@tonic-gate 
42227c478bd9Sstevel@tonic-gate 	if (on & O_CRMOD) {
42237c478bd9Sstevel@tonic-gate 		tios.c_iflag |= ICRNL;
42247c478bd9Sstevel@tonic-gate 		tios.c_oflag |= ONLCR;
42257c478bd9Sstevel@tonic-gate 	}
42267c478bd9Sstevel@tonic-gate 	/*
42277c478bd9Sstevel@tonic-gate 	 * Because "O_CRMOD" will never be set in "off" we don't have to
42287c478bd9Sstevel@tonic-gate 	 * handle this case here.
42297c478bd9Sstevel@tonic-gate 	 */
42307c478bd9Sstevel@tonic-gate 
42317c478bd9Sstevel@tonic-gate 	if (tcsetattr(pty, TCSANOW, &tios) < 0)
42327c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "tcsetattr: %m\n");
42337c478bd9Sstevel@tonic-gate }
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate /*
42367c478bd9Sstevel@tonic-gate  * Send interrupt to process on other side of pty.
42377c478bd9Sstevel@tonic-gate  * If it is in raw mode, just write NULL;
42387c478bd9Sstevel@tonic-gate  * otherwise, write intr char.
42397c478bd9Sstevel@tonic-gate  */
42407c478bd9Sstevel@tonic-gate static void
42417c478bd9Sstevel@tonic-gate interrupt(void)
42427c478bd9Sstevel@tonic-gate {
42437c478bd9Sstevel@tonic-gate 	struct sgttyb b;
42447c478bd9Sstevel@tonic-gate 	struct tchars tchars;
42457c478bd9Sstevel@tonic-gate 
42467c478bd9Sstevel@tonic-gate 	ptyflush();	/* half-hearted */
42477c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCGETP, &b) == -1)
42487c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP: %m\n");
42497c478bd9Sstevel@tonic-gate 	if (b.sg_flags & O_RAW) {
42507c478bd9Sstevel@tonic-gate 		*pfrontp++ = '\0';
42517c478bd9Sstevel@tonic-gate 		return;
42527c478bd9Sstevel@tonic-gate 	}
42537c478bd9Sstevel@tonic-gate 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
42547c478bd9Sstevel@tonic-gate 		'\177' : tchars.t_intrc;
42557c478bd9Sstevel@tonic-gate }
42567c478bd9Sstevel@tonic-gate 
42577c478bd9Sstevel@tonic-gate /*
42587c478bd9Sstevel@tonic-gate  * Send quit to process on other side of pty.
42597c478bd9Sstevel@tonic-gate  * If it is in raw mode, just write NULL;
42607c478bd9Sstevel@tonic-gate  * otherwise, write quit char.
42617c478bd9Sstevel@tonic-gate  */
42627c478bd9Sstevel@tonic-gate static void
42637c478bd9Sstevel@tonic-gate sendbrk(void)
42647c478bd9Sstevel@tonic-gate {
42657c478bd9Sstevel@tonic-gate 	struct sgttyb b;
42667c478bd9Sstevel@tonic-gate 	struct tchars tchars;
42677c478bd9Sstevel@tonic-gate 
42687c478bd9Sstevel@tonic-gate 	ptyflush();	/* half-hearted */
42697c478bd9Sstevel@tonic-gate 	(void) ioctl(pty, TIOCGETP, &b);
42707c478bd9Sstevel@tonic-gate 	if (b.sg_flags & O_RAW) {
42717c478bd9Sstevel@tonic-gate 		*pfrontp++ = '\0';
42727c478bd9Sstevel@tonic-gate 		return;
42737c478bd9Sstevel@tonic-gate 	}
42747c478bd9Sstevel@tonic-gate 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
42757c478bd9Sstevel@tonic-gate 		'\034' : tchars.t_quitc;
42767c478bd9Sstevel@tonic-gate }
42777c478bd9Sstevel@tonic-gate 
42787c478bd9Sstevel@tonic-gate static void
42797c478bd9Sstevel@tonic-gate ptyflush(void)
42807c478bd9Sstevel@tonic-gate {
42817c478bd9Sstevel@tonic-gate 	int n;
42827c478bd9Sstevel@tonic-gate 
42837c478bd9Sstevel@tonic-gate 	if ((n = pfrontp - pbackp) > 0)
42847c478bd9Sstevel@tonic-gate 		n = write(master, pbackp, n);
42857c478bd9Sstevel@tonic-gate 	if (n < 0)
42867c478bd9Sstevel@tonic-gate 		return;
42877c478bd9Sstevel@tonic-gate 	pbackp += n;
42887c478bd9Sstevel@tonic-gate 	if (pbackp == pfrontp)
42897c478bd9Sstevel@tonic-gate 		pbackp = pfrontp = ptyobuf;
42907c478bd9Sstevel@tonic-gate }
42917c478bd9Sstevel@tonic-gate 
42927c478bd9Sstevel@tonic-gate /*
42937c478bd9Sstevel@tonic-gate  * nextitem()
42947c478bd9Sstevel@tonic-gate  *
42957c478bd9Sstevel@tonic-gate  *	Return the address of the next "item" in the TELNET data
42967c478bd9Sstevel@tonic-gate  * stream.  This will be the address of the next character if
42977c478bd9Sstevel@tonic-gate  * the current address is a user data character, or it will
42987c478bd9Sstevel@tonic-gate  * be the address of the character following the TELNET command
42997c478bd9Sstevel@tonic-gate  * if the current address is a TELNET IAC ("I Am a Command")
43007c478bd9Sstevel@tonic-gate  * character.
43017c478bd9Sstevel@tonic-gate  */
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate static char *
43047c478bd9Sstevel@tonic-gate nextitem(char *current)
43057c478bd9Sstevel@tonic-gate {
43067c478bd9Sstevel@tonic-gate 	if ((*current&0xff) != IAC) {
43077c478bd9Sstevel@tonic-gate 		return (current+1);
43087c478bd9Sstevel@tonic-gate 	}
43097c478bd9Sstevel@tonic-gate 	switch (*(current+1)&0xff) {
43107c478bd9Sstevel@tonic-gate 	case DO:
43117c478bd9Sstevel@tonic-gate 	case DONT:
43127c478bd9Sstevel@tonic-gate 	case WILL:
43137c478bd9Sstevel@tonic-gate 	case WONT:
43147c478bd9Sstevel@tonic-gate 		return (current+3);
43157c478bd9Sstevel@tonic-gate 	case SB:		/* loop forever looking for the SE */
43167c478bd9Sstevel@tonic-gate 	{
43177c478bd9Sstevel@tonic-gate 		char *look = current+2;
43187c478bd9Sstevel@tonic-gate 
43197c478bd9Sstevel@tonic-gate 		for (;;) {
43207c478bd9Sstevel@tonic-gate 			if ((*look++&0xff) == IAC) {
43217c478bd9Sstevel@tonic-gate 				if ((*look++&0xff) == SE) {
43227c478bd9Sstevel@tonic-gate 					return (look);
43237c478bd9Sstevel@tonic-gate 				}
43247c478bd9Sstevel@tonic-gate 			}
43257c478bd9Sstevel@tonic-gate 		}
43267c478bd9Sstevel@tonic-gate 	}
43277c478bd9Sstevel@tonic-gate 	default:
43287c478bd9Sstevel@tonic-gate 		return (current+2);
43297c478bd9Sstevel@tonic-gate 	}
43307c478bd9Sstevel@tonic-gate }
43317c478bd9Sstevel@tonic-gate 
43327c478bd9Sstevel@tonic-gate 
43337c478bd9Sstevel@tonic-gate /*
43347c478bd9Sstevel@tonic-gate  * netclear()
43357c478bd9Sstevel@tonic-gate  *
43367c478bd9Sstevel@tonic-gate  *	We are about to do a TELNET SYNCH operation.  Clear
43377c478bd9Sstevel@tonic-gate  * the path to the network.
43387c478bd9Sstevel@tonic-gate  *
43397c478bd9Sstevel@tonic-gate  *	Things are a bit tricky since we may have sent the first
43407c478bd9Sstevel@tonic-gate  * byte or so of a previous TELNET command into the network.
43417c478bd9Sstevel@tonic-gate  * So, we have to scan the network buffer from the beginning
43427c478bd9Sstevel@tonic-gate  * until we are up to where we want to be.
43437c478bd9Sstevel@tonic-gate  *
43447c478bd9Sstevel@tonic-gate  *	A side effect of what we do, just to keep things
43457c478bd9Sstevel@tonic-gate  * simple, is to clear the urgent data pointer.  The principal
43467c478bd9Sstevel@tonic-gate  * caller should be setting the urgent data pointer AFTER calling
43477c478bd9Sstevel@tonic-gate  * us in any case.
43487c478bd9Sstevel@tonic-gate  */
43497c478bd9Sstevel@tonic-gate static void
43507c478bd9Sstevel@tonic-gate netclear(void)
43517c478bd9Sstevel@tonic-gate {
43527c478bd9Sstevel@tonic-gate 	char *thisitem, *next;
43537c478bd9Sstevel@tonic-gate 	char *good;
43547c478bd9Sstevel@tonic-gate #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
43557c478bd9Sstevel@tonic-gate 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate 	thisitem = netobuf;
43587c478bd9Sstevel@tonic-gate 
43597c478bd9Sstevel@tonic-gate 	while ((next = nextitem(thisitem)) <= nbackp) {
43607c478bd9Sstevel@tonic-gate 		thisitem = next;
43617c478bd9Sstevel@tonic-gate 	}
43627c478bd9Sstevel@tonic-gate 
43637c478bd9Sstevel@tonic-gate 	/* Now, thisitem is first before/at boundary. */
43647c478bd9Sstevel@tonic-gate 
43657c478bd9Sstevel@tonic-gate 	good = netobuf;	/* where the good bytes go */
43667c478bd9Sstevel@tonic-gate 
43677c478bd9Sstevel@tonic-gate 	while (nfrontp > thisitem) {
43687c478bd9Sstevel@tonic-gate 		if (wewant(thisitem)) {
43697c478bd9Sstevel@tonic-gate 			int length;
43707c478bd9Sstevel@tonic-gate 
43717c478bd9Sstevel@tonic-gate 			next = thisitem;
43727c478bd9Sstevel@tonic-gate 			do {
43737c478bd9Sstevel@tonic-gate 				next = nextitem(next);
43747c478bd9Sstevel@tonic-gate 			} while (wewant(next) && (nfrontp > next));
43757c478bd9Sstevel@tonic-gate 			length = next-thisitem;
43767c478bd9Sstevel@tonic-gate 			(void) memmove(good, thisitem, length);
43777c478bd9Sstevel@tonic-gate 			good += length;
43787c478bd9Sstevel@tonic-gate 			thisitem = next;
43797c478bd9Sstevel@tonic-gate 		} else {
43807c478bd9Sstevel@tonic-gate 			thisitem = nextitem(thisitem);
43817c478bd9Sstevel@tonic-gate 		}
43827c478bd9Sstevel@tonic-gate 	}
43837c478bd9Sstevel@tonic-gate 
43847c478bd9Sstevel@tonic-gate 	nbackp = netobuf;
43857c478bd9Sstevel@tonic-gate 	nfrontp = good;		/* next byte to be sent */
43867c478bd9Sstevel@tonic-gate 	neturg = 0;
43877c478bd9Sstevel@tonic-gate }
43887c478bd9Sstevel@tonic-gate 
43897c478bd9Sstevel@tonic-gate 
43907c478bd9Sstevel@tonic-gate /*
43917c478bd9Sstevel@tonic-gate  *  netflush
43927c478bd9Sstevel@tonic-gate  *		Send as much data as possible to the network,
43937c478bd9Sstevel@tonic-gate  *	handling requests for urgent data.
43947c478bd9Sstevel@tonic-gate  */
43957c478bd9Sstevel@tonic-gate static void
43967c478bd9Sstevel@tonic-gate netflush(void)
43977c478bd9Sstevel@tonic-gate {
43987c478bd9Sstevel@tonic-gate 	int n;
43997c478bd9Sstevel@tonic-gate 
44007c478bd9Sstevel@tonic-gate 	if ((n = nfrontp - nbackp) > 0) {
44017c478bd9Sstevel@tonic-gate 		/*
44027c478bd9Sstevel@tonic-gate 		 * if no urgent data, or if the other side appears to be an
44037c478bd9Sstevel@tonic-gate 		 * old 4.2 client (and thus unable to survive TCP urgent data),
44047c478bd9Sstevel@tonic-gate 		 * write the entire buffer in non-OOB mode.
44057c478bd9Sstevel@tonic-gate 		 */
44067c478bd9Sstevel@tonic-gate 		if ((neturg == 0) || (not42 == 0)) {
44077c478bd9Sstevel@tonic-gate 			n = write(net, nbackp, n);	/* normal write */
44087c478bd9Sstevel@tonic-gate 		} else {
44097c478bd9Sstevel@tonic-gate 			n = neturg - nbackp;
44107c478bd9Sstevel@tonic-gate 			/*
44117c478bd9Sstevel@tonic-gate 			 * In 4.2 (and 4.3) systems, there is some question
44127c478bd9Sstevel@tonic-gate 			 * about what byte in a sendOOB operation is the "OOB"
44137c478bd9Sstevel@tonic-gate 			 * data.  To make ourselves compatible, we only send ONE
44147c478bd9Sstevel@tonic-gate 			 * byte out of band, the one WE THINK should be OOB
44157c478bd9Sstevel@tonic-gate 			 * (though we really have more the TCP philosophy of
44167c478bd9Sstevel@tonic-gate 			 * urgent data rather than the Unix philosophy of OOB
44177c478bd9Sstevel@tonic-gate 			 * data).
44187c478bd9Sstevel@tonic-gate 			 */
44197c478bd9Sstevel@tonic-gate 			if (n > 1) {
44207c478bd9Sstevel@tonic-gate 				/* send URGENT all by itself */
44217c478bd9Sstevel@tonic-gate 				n = write(net, nbackp, n-1);
44227c478bd9Sstevel@tonic-gate 			} else {
44237c478bd9Sstevel@tonic-gate 				/* URGENT data */
44247c478bd9Sstevel@tonic-gate 				n = send_oob(net, nbackp, n);
44257c478bd9Sstevel@tonic-gate 			}
44267c478bd9Sstevel@tonic-gate 		}
44277c478bd9Sstevel@tonic-gate 	}
44287c478bd9Sstevel@tonic-gate 	if (n < 0) {
44297c478bd9Sstevel@tonic-gate 		if (errno == EWOULDBLOCK)
44307c478bd9Sstevel@tonic-gate 			return;
44317c478bd9Sstevel@tonic-gate 		/* should blow this guy away... */
44327c478bd9Sstevel@tonic-gate 		return;
44337c478bd9Sstevel@tonic-gate 	}
44347c478bd9Sstevel@tonic-gate 
44357c478bd9Sstevel@tonic-gate 	nbackp += n;
44367c478bd9Sstevel@tonic-gate 
44377c478bd9Sstevel@tonic-gate 	if (nbackp >= neturg) {
44387c478bd9Sstevel@tonic-gate 		neturg = 0;
44397c478bd9Sstevel@tonic-gate 	}
44407c478bd9Sstevel@tonic-gate 	if (nbackp == nfrontp) {
44417c478bd9Sstevel@tonic-gate 		nbackp = nfrontp = netobuf;
44427c478bd9Sstevel@tonic-gate 	}
44437c478bd9Sstevel@tonic-gate }
44447c478bd9Sstevel@tonic-gate 
44457c478bd9Sstevel@tonic-gate /* ARGSUSED */
44467c478bd9Sstevel@tonic-gate static void
44477c478bd9Sstevel@tonic-gate cleanup(int signum)
44487c478bd9Sstevel@tonic-gate {
44497c478bd9Sstevel@tonic-gate 	/*
44507c478bd9Sstevel@tonic-gate 	 * If the TEL_IOC_ENABLE ioctl hasn't completed, then we need to
44517c478bd9Sstevel@tonic-gate 	 * handle closing differently.  We close "net" first and then
44527c478bd9Sstevel@tonic-gate 	 * "master" in that order.  We do close(net) first because
44537c478bd9Sstevel@tonic-gate 	 * we have no other way to disconnect forwarding between the network
44547c478bd9Sstevel@tonic-gate 	 * and master.  So by issuing the close()'s we ensure that no further
44557c478bd9Sstevel@tonic-gate 	 * data rises from TCP.  A more complex fix would be adding proper
44567c478bd9Sstevel@tonic-gate 	 * support for throwing a "stop" switch for forwarding data between
44577c478bd9Sstevel@tonic-gate 	 * logindmux peers.  It's possible to block in the close of the tty
44587c478bd9Sstevel@tonic-gate 	 * while the network still receives data and the telmod module is
44597c478bd9Sstevel@tonic-gate 	 * TEL_STOPPED.  A denial-of-service attack generates this case,
44607c478bd9Sstevel@tonic-gate 	 * see 4102102.
44617c478bd9Sstevel@tonic-gate 	 */
44627c478bd9Sstevel@tonic-gate 
44637c478bd9Sstevel@tonic-gate 	if (!telmod_init_done) {
44647c478bd9Sstevel@tonic-gate 		(void) close(net);
44657c478bd9Sstevel@tonic-gate 		(void) close(master);
44667c478bd9Sstevel@tonic-gate 	}
44677c478bd9Sstevel@tonic-gate 	rmut();
44687c478bd9Sstevel@tonic-gate 
44697c478bd9Sstevel@tonic-gate 	exit(EXIT_FAILURE);
44707c478bd9Sstevel@tonic-gate }
44717c478bd9Sstevel@tonic-gate 
44727c478bd9Sstevel@tonic-gate static void
44737c478bd9Sstevel@tonic-gate rmut(void)
44747c478bd9Sstevel@tonic-gate {
44757c478bd9Sstevel@tonic-gate 	pam_handle_t    *pamh;
44767c478bd9Sstevel@tonic-gate 	struct utmpx *up;
44777c478bd9Sstevel@tonic-gate 	char user[sizeof (up->ut_user) + 1];
44787c478bd9Sstevel@tonic-gate 	char ttyn[sizeof (up->ut_line) + 1];
44797c478bd9Sstevel@tonic-gate 	char rhost[sizeof (up->ut_host) + 1];
44807c478bd9Sstevel@tonic-gate 
44817c478bd9Sstevel@tonic-gate 	/* while cleaning up don't allow disruption */
44827c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, SIG_IGN);
44837c478bd9Sstevel@tonic-gate 
44847c478bd9Sstevel@tonic-gate 	setutxent();
44857c478bd9Sstevel@tonic-gate 	while (up = getutxent()) {
44867c478bd9Sstevel@tonic-gate 		if (up->ut_pid == pid) {
44877c478bd9Sstevel@tonic-gate 			if (up->ut_type == DEAD_PROCESS) {
44887c478bd9Sstevel@tonic-gate 				/*
44897c478bd9Sstevel@tonic-gate 				 * Cleaned up elsewhere.
44907c478bd9Sstevel@tonic-gate 				 */
44917c478bd9Sstevel@tonic-gate 				break;
44927c478bd9Sstevel@tonic-gate 			}
44937c478bd9Sstevel@tonic-gate 
44947c478bd9Sstevel@tonic-gate 			/*
44957c478bd9Sstevel@tonic-gate 			 * call pam_close_session if login changed
44967c478bd9Sstevel@tonic-gate 			 * the utmpx user entry from type LOGIN_PROCESS
44977c478bd9Sstevel@tonic-gate 			 * to type USER_PROCESS, which happens
44987c478bd9Sstevel@tonic-gate 			 * after pam_open_session is called.
44997c478bd9Sstevel@tonic-gate 			 */
45007c478bd9Sstevel@tonic-gate 			if (up->ut_type == USER_PROCESS) {
45017c478bd9Sstevel@tonic-gate 				(void) strlcpy(user, up->ut_user,
45027c478bd9Sstevel@tonic-gate 					    sizeof (user));
45037c478bd9Sstevel@tonic-gate 				(void) strlcpy(ttyn, up->ut_line,
45047c478bd9Sstevel@tonic-gate 					    sizeof (ttyn));
45057c478bd9Sstevel@tonic-gate 				(void) strlcpy(rhost, up->ut_host,
45067c478bd9Sstevel@tonic-gate 					    sizeof (rhost));
45077c478bd9Sstevel@tonic-gate 				if ((pam_start("telnet", user, NULL, &pamh)) ==
45087c478bd9Sstevel@tonic-gate 				    PAM_SUCCESS) {
45097c478bd9Sstevel@tonic-gate 					(void) pam_set_item(pamh, PAM_TTY,
45107c478bd9Sstevel@tonic-gate 							    ttyn);
45117c478bd9Sstevel@tonic-gate 					(void) pam_set_item(pamh, PAM_RHOST,
45127c478bd9Sstevel@tonic-gate 							    rhost);
45137c478bd9Sstevel@tonic-gate 					(void) pam_close_session(pamh, 0);
45147c478bd9Sstevel@tonic-gate 					(void) pam_end(pamh, PAM_SUCCESS);
45157c478bd9Sstevel@tonic-gate 				}
45167c478bd9Sstevel@tonic-gate 			}
45177c478bd9Sstevel@tonic-gate 
45187c478bd9Sstevel@tonic-gate 			up->ut_type = DEAD_PROCESS;
45197c478bd9Sstevel@tonic-gate 			up->ut_exit.e_termination = WTERMSIG(0);
45207c478bd9Sstevel@tonic-gate 			up->ut_exit.e_exit = WEXITSTATUS(0);
45217c478bd9Sstevel@tonic-gate 			(void) time(&up->ut_tv.tv_sec);
45227c478bd9Sstevel@tonic-gate 
45237c478bd9Sstevel@tonic-gate 			if (modutx(up) == NULL) {
45247c478bd9Sstevel@tonic-gate 				/*
45257c478bd9Sstevel@tonic-gate 				 * Since modutx failed we'll
45267c478bd9Sstevel@tonic-gate 				 * write out the new entry
45277c478bd9Sstevel@tonic-gate 				 * ourselves.
45287c478bd9Sstevel@tonic-gate 				 */
45297c478bd9Sstevel@tonic-gate 				(void) pututxline(up);
45307c478bd9Sstevel@tonic-gate 				updwtmpx("wtmpx", up);
45317c478bd9Sstevel@tonic-gate 			}
45327c478bd9Sstevel@tonic-gate 			break;
45337c478bd9Sstevel@tonic-gate 		}
45347c478bd9Sstevel@tonic-gate 	}
45357c478bd9Sstevel@tonic-gate 
45367c478bd9Sstevel@tonic-gate 	endutxent();
45377c478bd9Sstevel@tonic-gate 
45387c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, (void (*)())cleanup);
45397c478bd9Sstevel@tonic-gate }
45407c478bd9Sstevel@tonic-gate 
45417c478bd9Sstevel@tonic-gate static int
45427c478bd9Sstevel@tonic-gate readstream(int fd, char *buf, int offset)
45437c478bd9Sstevel@tonic-gate {
45447c478bd9Sstevel@tonic-gate 	struct strbuf ctlbuf, datbuf;
45457c478bd9Sstevel@tonic-gate 	union T_primitives tpi;
45467c478bd9Sstevel@tonic-gate 	int	ret = 0;
45477c478bd9Sstevel@tonic-gate 	int	flags = 0;
45487c478bd9Sstevel@tonic-gate 	int	bytes_avail, count;
45497c478bd9Sstevel@tonic-gate 
45507c478bd9Sstevel@tonic-gate 	(void) memset((char *)&ctlbuf, 0, sizeof (ctlbuf));
45517c478bd9Sstevel@tonic-gate 	(void) memset((char *)&datbuf, 0, sizeof (datbuf));
45527c478bd9Sstevel@tonic-gate 
45537c478bd9Sstevel@tonic-gate 	ctlbuf.buf = (char *)&tpi;
45547c478bd9Sstevel@tonic-gate 	ctlbuf.maxlen = sizeof (tpi);
45557c478bd9Sstevel@tonic-gate 
45567c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_NREAD, &bytes_avail) < 0) {
45577c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "I_NREAD returned error %m");
45587c478bd9Sstevel@tonic-gate 		return (-1);
45597c478bd9Sstevel@tonic-gate 	}
45607c478bd9Sstevel@tonic-gate 	if (bytes_avail > netibufsize - offset) {
45617c478bd9Sstevel@tonic-gate 		count = netip - netibuf;
45627c478bd9Sstevel@tonic-gate 		netibuf = (char *)realloc(netibuf,
45637c478bd9Sstevel@tonic-gate 		    (unsigned)netibufsize + bytes_avail);
45647c478bd9Sstevel@tonic-gate 		if (netibuf == NULL) {
45657c478bd9Sstevel@tonic-gate 			fatal(net, "netibuf realloc failed\n");
45667c478bd9Sstevel@tonic-gate 		}
45677c478bd9Sstevel@tonic-gate 		netibufsize += bytes_avail;
45687c478bd9Sstevel@tonic-gate 		netip = netibuf + count;
45697c478bd9Sstevel@tonic-gate 		buf = netibuf;
45707c478bd9Sstevel@tonic-gate 	}
45717c478bd9Sstevel@tonic-gate 	datbuf.buf = buf + offset;
45727c478bd9Sstevel@tonic-gate 	datbuf.maxlen = netibufsize;
45737c478bd9Sstevel@tonic-gate 	ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
45747c478bd9Sstevel@tonic-gate 	if (ret < 0) {
45757c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "getmsg returned -1, errno %d\n",
45767c478bd9Sstevel@tonic-gate 			errno);
45777c478bd9Sstevel@tonic-gate 		return (-1);
45787c478bd9Sstevel@tonic-gate 	}
45797c478bd9Sstevel@tonic-gate 	if (ctlbuf.len <= 0) {
45807c478bd9Sstevel@tonic-gate 		return (datbuf.len);
45817c478bd9Sstevel@tonic-gate 	}
45827c478bd9Sstevel@tonic-gate 
45837c478bd9Sstevel@tonic-gate 	if (tpi.type == T_DATA_REQ) {
45847c478bd9Sstevel@tonic-gate 		return (0);
45857c478bd9Sstevel@tonic-gate 	}
45867c478bd9Sstevel@tonic-gate 
45877c478bd9Sstevel@tonic-gate 	if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
45887c478bd9Sstevel@tonic-gate 		cleanup(0);
45897c478bd9Sstevel@tonic-gate 	fatal(fd, "no data or protocol element recognized");
4590740638c8Sbw 	return (0);
45917c478bd9Sstevel@tonic-gate }
45927c478bd9Sstevel@tonic-gate 
45937c478bd9Sstevel@tonic-gate static void
45947c478bd9Sstevel@tonic-gate drainstream(int size)
45957c478bd9Sstevel@tonic-gate {
45967c478bd9Sstevel@tonic-gate 	int	nbytes;
45977c478bd9Sstevel@tonic-gate 	int	tsize;
45987c478bd9Sstevel@tonic-gate 
45997c478bd9Sstevel@tonic-gate 	tsize = netip - netibuf;
46007c478bd9Sstevel@tonic-gate 
46017c478bd9Sstevel@tonic-gate 	if ((tsize + ncc + size) > netibufsize) {
46027c478bd9Sstevel@tonic-gate 		if (!(netibuf = (char *)realloc(netibuf,
46037c478bd9Sstevel@tonic-gate 		    (unsigned)tsize + ncc + size)))
46047c478bd9Sstevel@tonic-gate 			fatalperror(net, "netibuf realloc failed\n", errno);
46057c478bd9Sstevel@tonic-gate 		netibufsize = tsize + ncc + size;
46067c478bd9Sstevel@tonic-gate 
46077c478bd9Sstevel@tonic-gate 		netip = netibuf + tsize;
46087c478bd9Sstevel@tonic-gate 	}
46097c478bd9Sstevel@tonic-gate 
46107c478bd9Sstevel@tonic-gate 	if ((nbytes = read(net, (char *)netip + ncc, size)) != size)
46117c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "read %d bytes\n", nbytes);
46127c478bd9Sstevel@tonic-gate }
46137c478bd9Sstevel@tonic-gate 
46147c478bd9Sstevel@tonic-gate /*
46157c478bd9Sstevel@tonic-gate  * TPI style replacement for socket send() primitive, so we don't require
46167c478bd9Sstevel@tonic-gate  * sockmod to be on the stream.
46177c478bd9Sstevel@tonic-gate  */
46187c478bd9Sstevel@tonic-gate static int
46197c478bd9Sstevel@tonic-gate send_oob(int fd, char *ptr, int count)
46207c478bd9Sstevel@tonic-gate {
46217c478bd9Sstevel@tonic-gate 	struct T_exdata_req exd_req;
46227c478bd9Sstevel@tonic-gate 	struct strbuf hdr, dat;
46237c478bd9Sstevel@tonic-gate 	int ret;
46247c478bd9Sstevel@tonic-gate 
46257c478bd9Sstevel@tonic-gate 	exd_req.PRIM_type = T_EXDATA_REQ;
46267c478bd9Sstevel@tonic-gate 	exd_req.MORE_flag = 0;
46277c478bd9Sstevel@tonic-gate 
46287c478bd9Sstevel@tonic-gate 	hdr.buf = (char *)&exd_req;
46297c478bd9Sstevel@tonic-gate 	hdr.len = sizeof (exd_req);
46307c478bd9Sstevel@tonic-gate 
46317c478bd9Sstevel@tonic-gate 	dat.buf = ptr;
46327c478bd9Sstevel@tonic-gate 	dat.len = count;
46337c478bd9Sstevel@tonic-gate 
46347c478bd9Sstevel@tonic-gate 	ret = putmsg(fd, &hdr, &dat, 0);
46357c478bd9Sstevel@tonic-gate 	if (ret == 0) {
46367c478bd9Sstevel@tonic-gate 		ret = count;
46377c478bd9Sstevel@tonic-gate 	}
46387c478bd9Sstevel@tonic-gate 	return (ret);
46397c478bd9Sstevel@tonic-gate }
46407c478bd9Sstevel@tonic-gate 
46417c478bd9Sstevel@tonic-gate 
46427c478bd9Sstevel@tonic-gate /*
46437c478bd9Sstevel@tonic-gate  * local_setenv --
46447c478bd9Sstevel@tonic-gate  *	Set the value of the environmental variable "name" to be
46457c478bd9Sstevel@tonic-gate  *	"value".  If rewrite is set, replace any current value.
46467c478bd9Sstevel@tonic-gate  */
46477c478bd9Sstevel@tonic-gate static int
46487c478bd9Sstevel@tonic-gate local_setenv(const char *name, const char *value, int rewrite)
46497c478bd9Sstevel@tonic-gate {
46507c478bd9Sstevel@tonic-gate 	static int alloced;			/* if allocated space before */
46517c478bd9Sstevel@tonic-gate 	char *c;
46527c478bd9Sstevel@tonic-gate 	int l_value, offset;
46537c478bd9Sstevel@tonic-gate 
46547c478bd9Sstevel@tonic-gate 	/*
46557c478bd9Sstevel@tonic-gate 	 * Do not allow environment variables which begin with LD_ to be
46567c478bd9Sstevel@tonic-gate 	 * inserted into the environment.  While normally the dynamic linker
46577c478bd9Sstevel@tonic-gate 	 * protects the login program, that is based on the assumption hostile
46587c478bd9Sstevel@tonic-gate 	 * invocation of login are from non-root users.  However, since telnetd
46597c478bd9Sstevel@tonic-gate 	 * runs as root, this cannot be utilized.  So instead we simply
46607c478bd9Sstevel@tonic-gate 	 * prevent LD_* from being inserted into the environment.
46617c478bd9Sstevel@tonic-gate 	 * This also applies to other environment variables that
46627c478bd9Sstevel@tonic-gate 	 * are to be ignored in setugid apps.
46637c478bd9Sstevel@tonic-gate 	 * Note that at this point name can contain '='!
46647c478bd9Sstevel@tonic-gate 	 * Also, do not allow TTYPROMPT to be passed along here.
46657c478bd9Sstevel@tonic-gate 	 */
46667c478bd9Sstevel@tonic-gate 	if (strncmp(name, "LD_", 3) == 0 ||
46677c478bd9Sstevel@tonic-gate 	    strncmp(name, "NLSPATH", 7) == 0 ||
46687c478bd9Sstevel@tonic-gate 	    (strncmp(name, "TTYPROMPT", 9) == 0 &&
46697c478bd9Sstevel@tonic-gate 		(name[9] == '\0' || name[9] == '='))) {
46707c478bd9Sstevel@tonic-gate 		return (-1);
46717c478bd9Sstevel@tonic-gate 	}
46727c478bd9Sstevel@tonic-gate 	if (*value == '=')			/* no `=' in value */
46737c478bd9Sstevel@tonic-gate 		++value;
46747c478bd9Sstevel@tonic-gate 	l_value = strlen(value);
46757c478bd9Sstevel@tonic-gate 	if ((c = __findenv(name, &offset))) {	/* find if already exists */
46767c478bd9Sstevel@tonic-gate 		if (!rewrite)
46777c478bd9Sstevel@tonic-gate 			return (0);
46787c478bd9Sstevel@tonic-gate 		if ((int)strlen(c) >= l_value) { /* old larger; copy over */
46797c478bd9Sstevel@tonic-gate 			while (*c++ = *value++);
46807c478bd9Sstevel@tonic-gate 			return (0);
46817c478bd9Sstevel@tonic-gate 		}
46827c478bd9Sstevel@tonic-gate 	} else {					/* create new slot */
46837c478bd9Sstevel@tonic-gate 		int cnt;
46847c478bd9Sstevel@tonic-gate 		char **p;
46857c478bd9Sstevel@tonic-gate 
46867c478bd9Sstevel@tonic-gate 		for (p = environ, cnt = 0; *p; ++p, ++cnt);
46877c478bd9Sstevel@tonic-gate 		if (alloced) {			/* just increase size */
46887c478bd9Sstevel@tonic-gate 			environ = (char **)realloc((char *)environ,
46897c478bd9Sstevel@tonic-gate 			    (size_t)(sizeof (char *) * (cnt + 2)));
46907c478bd9Sstevel@tonic-gate 			if (!environ)
46917c478bd9Sstevel@tonic-gate 				return (-1);
46927c478bd9Sstevel@tonic-gate 		} else {				/* get new space */
46937c478bd9Sstevel@tonic-gate 			alloced = 1;		/* copy old entries into it */
46947c478bd9Sstevel@tonic-gate 			p = (char **)malloc((size_t)(sizeof (char *)*
46957c478bd9Sstevel@tonic-gate 			    (cnt + 2)));
46967c478bd9Sstevel@tonic-gate 			if (!p)
46977c478bd9Sstevel@tonic-gate 				return (-1);
46987c478bd9Sstevel@tonic-gate 			(void) memcpy(p, environ, cnt * sizeof (char *));
46997c478bd9Sstevel@tonic-gate 			environ = p;
47007c478bd9Sstevel@tonic-gate 		}
47017c478bd9Sstevel@tonic-gate 		environ[cnt + 1] = NULL;
47027c478bd9Sstevel@tonic-gate 		offset = cnt;
47037c478bd9Sstevel@tonic-gate 	}
47047c478bd9Sstevel@tonic-gate 	for (c = (char *)name; *c && *c != '='; ++c);	/* no `=' in name */
47057c478bd9Sstevel@tonic-gate 	if (!(environ[offset] =			/* name + `=' + value */
47067c478bd9Sstevel@tonic-gate 	    malloc((size_t)((int)(c - name) + l_value + 2))))
47077c478bd9Sstevel@tonic-gate 		return (-1);
47087c478bd9Sstevel@tonic-gate 	for (c = environ[offset]; ((*c = *name++) != 0) && (*c != '='); ++c);
47097c478bd9Sstevel@tonic-gate 	for (*c++ = '='; *c++ = *value++; );
47107c478bd9Sstevel@tonic-gate 	return (0);
47117c478bd9Sstevel@tonic-gate }
47127c478bd9Sstevel@tonic-gate 
47137c478bd9Sstevel@tonic-gate /*
47147c478bd9Sstevel@tonic-gate  * local_unsetenv(name) --
47157c478bd9Sstevel@tonic-gate  *	Delete environmental variable "name".
47167c478bd9Sstevel@tonic-gate  */
47177c478bd9Sstevel@tonic-gate static void
47187c478bd9Sstevel@tonic-gate local_unsetenv(const char *name)
47197c478bd9Sstevel@tonic-gate {
47207c478bd9Sstevel@tonic-gate 	char **p;
47217c478bd9Sstevel@tonic-gate 	int offset;
47227c478bd9Sstevel@tonic-gate 
47237c478bd9Sstevel@tonic-gate 	while (__findenv(name, &offset))	/* if set multiple times */
47247c478bd9Sstevel@tonic-gate 		for (p = &environ[offset]; ; ++p)
47257c478bd9Sstevel@tonic-gate 			if ((*p = *(p + 1)) == 0)
47267c478bd9Sstevel@tonic-gate 				break;
47277c478bd9Sstevel@tonic-gate }
47287c478bd9Sstevel@tonic-gate 
47297c478bd9Sstevel@tonic-gate /*
47307c478bd9Sstevel@tonic-gate  * __findenv --
47317c478bd9Sstevel@tonic-gate  *	Returns pointer to value associated with name, if any, else NULL.
47327c478bd9Sstevel@tonic-gate  *	Sets offset to be the offset of the name/value combination in the
47337c478bd9Sstevel@tonic-gate  *	environmental array, for use by local_setenv() and local_unsetenv().
47347c478bd9Sstevel@tonic-gate  *	Explicitly removes '=' in argument name.
47357c478bd9Sstevel@tonic-gate  */
47367c478bd9Sstevel@tonic-gate static char *
47377c478bd9Sstevel@tonic-gate __findenv(const char *name, int *offset)
47387c478bd9Sstevel@tonic-gate {
47397c478bd9Sstevel@tonic-gate 	extern char **environ;
47407c478bd9Sstevel@tonic-gate 	int len;
47417c478bd9Sstevel@tonic-gate 	const char *np;
47427c478bd9Sstevel@tonic-gate 	char **p, *c;
47437c478bd9Sstevel@tonic-gate 
47447c478bd9Sstevel@tonic-gate 	if (name == NULL || environ == NULL)
47457c478bd9Sstevel@tonic-gate 		return (NULL);
47467c478bd9Sstevel@tonic-gate 	for (np = name; *np && *np != '='; ++np)
47477c478bd9Sstevel@tonic-gate 		continue;
47487c478bd9Sstevel@tonic-gate 	len = np - name;
47497c478bd9Sstevel@tonic-gate 	for (p = environ; (c = *p) != NULL; ++p)
47507c478bd9Sstevel@tonic-gate 		if (strncmp(c, name, len) == 0 && c[len] == '=') {
47517c478bd9Sstevel@tonic-gate 			*offset = p - environ;
47527c478bd9Sstevel@tonic-gate 			return (c + len + 1);
47537c478bd9Sstevel@tonic-gate 		}
47547c478bd9Sstevel@tonic-gate 	return (NULL);
47557c478bd9Sstevel@tonic-gate }
47567c478bd9Sstevel@tonic-gate 
47577c478bd9Sstevel@tonic-gate static void
47587c478bd9Sstevel@tonic-gate showbanner(void)
47597c478bd9Sstevel@tonic-gate {
47607c478bd9Sstevel@tonic-gate 	char	*cp;
47617c478bd9Sstevel@tonic-gate 	char	evalbuf[BUFSIZ];
47627c478bd9Sstevel@tonic-gate 
47637c478bd9Sstevel@tonic-gate 	if (defopen(defaultfile) == 0) {
47647c478bd9Sstevel@tonic-gate 		int	flags;
47657c478bd9Sstevel@tonic-gate 
47667c478bd9Sstevel@tonic-gate 		/* ignore case */
47677c478bd9Sstevel@tonic-gate 		flags = defcntl(DC_GETFLAGS, 0);
47687c478bd9Sstevel@tonic-gate 		TURNOFF(flags, DC_CASE);
476906e1a714Sraf 		(void) defcntl(DC_SETFLAGS, flags);
47707c478bd9Sstevel@tonic-gate 		if (cp = defread(bannervar)) {
47717c478bd9Sstevel@tonic-gate 			FILE	*fp;
47727c478bd9Sstevel@tonic-gate 
47737c478bd9Sstevel@tonic-gate 			if (strlen(cp) + strlen("eval echo '") + strlen("'\n")
47747c478bd9Sstevel@tonic-gate 			    + 1 < sizeof (evalbuf)) {
47757c478bd9Sstevel@tonic-gate 				(void) strlcpy(evalbuf, "eval echo '",
47767c478bd9Sstevel@tonic-gate 					sizeof (evalbuf));
47777c478bd9Sstevel@tonic-gate 				(void) strlcat(evalbuf, cp, sizeof (evalbuf));
47787c478bd9Sstevel@tonic-gate 				(void) strlcat(evalbuf, "'\n",
47797c478bd9Sstevel@tonic-gate 						sizeof (evalbuf));
47807c478bd9Sstevel@tonic-gate 
47817c478bd9Sstevel@tonic-gate 				if (fp = popen(evalbuf, "r")) {
47827c478bd9Sstevel@tonic-gate 					char	buf[BUFSIZ];
47837c478bd9Sstevel@tonic-gate 					size_t	size;
47847c478bd9Sstevel@tonic-gate 
47857c478bd9Sstevel@tonic-gate 					/*
47867c478bd9Sstevel@tonic-gate 					 * Pipe I/O atomicity guarantees we
47877c478bd9Sstevel@tonic-gate 					 * need only one read.
47887c478bd9Sstevel@tonic-gate 					 */
47897c478bd9Sstevel@tonic-gate 					if ((size = fread(buf, 1,
47907c478bd9Sstevel@tonic-gate 							sizeof (buf) - 1,
47917c478bd9Sstevel@tonic-gate 							fp)) != 0) {
47927c478bd9Sstevel@tonic-gate 						char	*p;
47937c478bd9Sstevel@tonic-gate 						buf[size] = '\0';
47947c478bd9Sstevel@tonic-gate 						p = strrchr(buf, '\n');
47957c478bd9Sstevel@tonic-gate 						if (p != NULL)
47967c478bd9Sstevel@tonic-gate 							*p = '\0';
47977c478bd9Sstevel@tonic-gate 						if (strlen(buf)) {
47987c478bd9Sstevel@tonic-gate 							map_banner(buf);
47997c478bd9Sstevel@tonic-gate 							netflush();
48007c478bd9Sstevel@tonic-gate 						}
48017c478bd9Sstevel@tonic-gate 					}
48027c478bd9Sstevel@tonic-gate 					(void) pclose(fp);
48037c478bd9Sstevel@tonic-gate 					/* close default file */
48047c478bd9Sstevel@tonic-gate 					(void) defopen(NULL);
48057c478bd9Sstevel@tonic-gate 					return;
48067c478bd9Sstevel@tonic-gate 				}
48077c478bd9Sstevel@tonic-gate 			}
48087c478bd9Sstevel@tonic-gate 		}
48097c478bd9Sstevel@tonic-gate 		(void) defopen(NULL);	/* close default file */
48107c478bd9Sstevel@tonic-gate 	}
48117c478bd9Sstevel@tonic-gate 
48127c478bd9Sstevel@tonic-gate 	defbanner();
48137c478bd9Sstevel@tonic-gate 	netflush();
48147c478bd9Sstevel@tonic-gate }
48157c478bd9Sstevel@tonic-gate 
48167c478bd9Sstevel@tonic-gate static void
48177c478bd9Sstevel@tonic-gate map_banner(char *p)
48187c478bd9Sstevel@tonic-gate {
48197c478bd9Sstevel@tonic-gate 	char	*q;
48207c478bd9Sstevel@tonic-gate 
48217c478bd9Sstevel@tonic-gate 	/*
48227c478bd9Sstevel@tonic-gate 	 *	Map the banner:  "\n" -> "\r\n" and "\r" -> "\r\0"
48237c478bd9Sstevel@tonic-gate 	 */
48247c478bd9Sstevel@tonic-gate 	for (q = nfrontp; p && *p && q < nfrontp + sizeof (netobuf) - 1; )
48257c478bd9Sstevel@tonic-gate 		if (*p == '\n') {
48267c478bd9Sstevel@tonic-gate 			*q++ = '\r';
48277c478bd9Sstevel@tonic-gate 			*q++ = '\n';
48287c478bd9Sstevel@tonic-gate 			p++;
48297c478bd9Sstevel@tonic-gate 		} else if (*p == '\r') {
48307c478bd9Sstevel@tonic-gate 			*q++ = '\r';
48317c478bd9Sstevel@tonic-gate 			*q++ = '\0';
48327c478bd9Sstevel@tonic-gate 			p++;
48337c478bd9Sstevel@tonic-gate 		} else
48347c478bd9Sstevel@tonic-gate 			*q++ = *p++;
48357c478bd9Sstevel@tonic-gate 
48367c478bd9Sstevel@tonic-gate 	nfrontp += q - netobuf;
48377c478bd9Sstevel@tonic-gate }
48387c478bd9Sstevel@tonic-gate 
48397c478bd9Sstevel@tonic-gate /*
48407c478bd9Sstevel@tonic-gate  * Show banner that getty never gave.  By default, this is `uname -sr`.
48417c478bd9Sstevel@tonic-gate  *
48427c478bd9Sstevel@tonic-gate  * The banner includes some null's (for TELNET CR disambiguation),
48437c478bd9Sstevel@tonic-gate  * so we have to be somewhat complicated.
48447c478bd9Sstevel@tonic-gate  */
48457c478bd9Sstevel@tonic-gate static void
48467c478bd9Sstevel@tonic-gate defbanner(void)
48477c478bd9Sstevel@tonic-gate {
48487c478bd9Sstevel@tonic-gate 	struct utsname u;
48497c478bd9Sstevel@tonic-gate 
48507c478bd9Sstevel@tonic-gate 	/*
48517c478bd9Sstevel@tonic-gate 	 * Dont show this if the '-h' option was present
48527c478bd9Sstevel@tonic-gate 	 */
48537c478bd9Sstevel@tonic-gate 	if (!show_hostinfo)
48547c478bd9Sstevel@tonic-gate 		return;
48557c478bd9Sstevel@tonic-gate 
48567c478bd9Sstevel@tonic-gate 	if (uname(&u) == -1)
48577c478bd9Sstevel@tonic-gate 		return;
48587c478bd9Sstevel@tonic-gate 
48597c478bd9Sstevel@tonic-gate 	write_data_len((const char *) BANNER1, sizeof (BANNER1) - 1);
48607c478bd9Sstevel@tonic-gate 	write_data_len(u.sysname, strlen(u.sysname));
48617c478bd9Sstevel@tonic-gate 	write_data_len(" ", 1);
48627c478bd9Sstevel@tonic-gate 	write_data_len(u.release, strlen(u.release));
48637c478bd9Sstevel@tonic-gate 	write_data_len((const char *)BANNER2, sizeof (BANNER2) - 1);
48647c478bd9Sstevel@tonic-gate }
48657c478bd9Sstevel@tonic-gate 
48667c478bd9Sstevel@tonic-gate /*
48677c478bd9Sstevel@tonic-gate  * Verify that the named module is at the top of the stream
48687c478bd9Sstevel@tonic-gate  * and then pop it off.
48697c478bd9Sstevel@tonic-gate  */
48707c478bd9Sstevel@tonic-gate static int
48717c478bd9Sstevel@tonic-gate removemod(int f, char *modname)
48727c478bd9Sstevel@tonic-gate {
48737c478bd9Sstevel@tonic-gate 	char topmodname[BUFSIZ];
48747c478bd9Sstevel@tonic-gate 
48757c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_LOOK, topmodname) < 0)
48767c478bd9Sstevel@tonic-gate 		return (-1);
48777c478bd9Sstevel@tonic-gate 	if (strcmp(modname, topmodname) != 0) {
48787c478bd9Sstevel@tonic-gate 		errno = ENXIO;
48797c478bd9Sstevel@tonic-gate 		return (-1);
48807c478bd9Sstevel@tonic-gate 	}
48817c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_POP, 0) < 0)
48827c478bd9Sstevel@tonic-gate 		return (-1);
48837c478bd9Sstevel@tonic-gate 	return (0);
48847c478bd9Sstevel@tonic-gate }
48857c478bd9Sstevel@tonic-gate 
48867c478bd9Sstevel@tonic-gate static void
48877c478bd9Sstevel@tonic-gate write_data(const char *format, ...)
48887c478bd9Sstevel@tonic-gate {
48897c478bd9Sstevel@tonic-gate 	va_list args;
48907c478bd9Sstevel@tonic-gate 	int		len;
48917c478bd9Sstevel@tonic-gate 	char	argp[BUFSIZ];
48927c478bd9Sstevel@tonic-gate 
48937c478bd9Sstevel@tonic-gate 	va_start(args, format);
48947c478bd9Sstevel@tonic-gate 
48957c478bd9Sstevel@tonic-gate 	if ((len = vsnprintf(argp, sizeof (argp), format, args)) == -1)
48967c478bd9Sstevel@tonic-gate 		return;
48977c478bd9Sstevel@tonic-gate 
48987c478bd9Sstevel@tonic-gate 	write_data_len(argp, len);
48997c478bd9Sstevel@tonic-gate 	va_end(args);
49007c478bd9Sstevel@tonic-gate }
49017c478bd9Sstevel@tonic-gate 
49027c478bd9Sstevel@tonic-gate static void
49037c478bd9Sstevel@tonic-gate write_data_len(const char *buf, int len)
49047c478bd9Sstevel@tonic-gate {
49057c478bd9Sstevel@tonic-gate 	int remaining, copied;
49067c478bd9Sstevel@tonic-gate 
49077c478bd9Sstevel@tonic-gate 	remaining = BUFSIZ - (nfrontp - netobuf);
49087c478bd9Sstevel@tonic-gate 	while (len > 0) {
49097c478bd9Sstevel@tonic-gate 		/*
49107c478bd9Sstevel@tonic-gate 		 * If there's not enough space in netobuf then
49117c478bd9Sstevel@tonic-gate 		 * try to make some.
49127c478bd9Sstevel@tonic-gate 		 */
49137c478bd9Sstevel@tonic-gate 	if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
49147c478bd9Sstevel@tonic-gate 			netflush();
49157c478bd9Sstevel@tonic-gate 			remaining = BUFSIZ - (nfrontp - netobuf);
49167c478bd9Sstevel@tonic-gate 		}
49177c478bd9Sstevel@tonic-gate 		/* Copy as much as we can */
49187c478bd9Sstevel@tonic-gate 		copied = remaining > len ? len : remaining;
49197c478bd9Sstevel@tonic-gate 		(void) memmove(nfrontp, buf, copied);
49207c478bd9Sstevel@tonic-gate 		nfrontp += copied;
49217c478bd9Sstevel@tonic-gate 		len -= copied;
49227c478bd9Sstevel@tonic-gate 		remaining -= copied;
49237c478bd9Sstevel@tonic-gate 		buf += copied;
49247c478bd9Sstevel@tonic-gate 	}
49257c478bd9Sstevel@tonic-gate }
4926