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