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