xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/in.telnetd.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  *
22*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
23*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*7c478bd9Sstevel@tonic-gate  */
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate /*
27*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
28*7c478bd9Sstevel@tonic-gate  * All Rights Reserved.
29*7c478bd9Sstevel@tonic-gate  */
30*7c478bd9Sstevel@tonic-gate 
31*7c478bd9Sstevel@tonic-gate /*
32*7c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
33*7c478bd9Sstevel@tonic-gate  * The Regents of the University of California.
34*7c478bd9Sstevel@tonic-gate  * All Rights Reserved.
35*7c478bd9Sstevel@tonic-gate  *
36*7c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
37*7c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
38*7c478bd9Sstevel@tonic-gate  * contributors.
39*7c478bd9Sstevel@tonic-gate  */
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate /*
44*7c478bd9Sstevel@tonic-gate  * Telnet server.
45*7c478bd9Sstevel@tonic-gate  */
46*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/socket.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/wait.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/filio.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/stream.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
58*7c478bd9Sstevel@tonic-gate #include <unistd.h>
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate #include <netinet/in.h>
61*7c478bd9Sstevel@tonic-gate 
62*7c478bd9Sstevel@tonic-gate #define	AUTHWHO_STR
63*7c478bd9Sstevel@tonic-gate #define	AUTHTYPE_NAMES
64*7c478bd9Sstevel@tonic-gate #define	AUTHHOW_NAMES
65*7c478bd9Sstevel@tonic-gate #define	AUTHRSP_NAMES
66*7c478bd9Sstevel@tonic-gate #define	ENCRYPT_NAMES
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate #include <arpa/telnet.h>
69*7c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
70*7c478bd9Sstevel@tonic-gate #include <stdio.h>
71*7c478bd9Sstevel@tonic-gate #include <stdarg.h>
72*7c478bd9Sstevel@tonic-gate #include <signal.h>
73*7c478bd9Sstevel@tonic-gate #include <errno.h>
74*7c478bd9Sstevel@tonic-gate #include <netdb.h>
75*7c478bd9Sstevel@tonic-gate #include <syslog.h>
76*7c478bd9Sstevel@tonic-gate #include <ctype.h>
77*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
78*7c478bd9Sstevel@tonic-gate #include <sac.h>	/* for SC_WILDC */
79*7c478bd9Sstevel@tonic-gate #include <utmpx.h>
80*7c478bd9Sstevel@tonic-gate #include <sys/ttold.h>
81*7c478bd9Sstevel@tonic-gate #include <malloc.h>
82*7c478bd9Sstevel@tonic-gate #include <string.h>
83*7c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
84*7c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
85*7c478bd9Sstevel@tonic-gate #include <sys/logindmux.h>
86*7c478bd9Sstevel@tonic-gate #include <sys/telioctl.h>
87*7c478bd9Sstevel@tonic-gate #include <deflt.h>
88*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
89*7c478bd9Sstevel@tonic-gate #include <string.h>
90*7c478bd9Sstevel@tonic-gate #include <stropts.h>
91*7c478bd9Sstevel@tonic-gate #include <termios.h>
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate #include <com_err.h>
94*7c478bd9Sstevel@tonic-gate #include <krb5.h>
95*7c478bd9Sstevel@tonic-gate #include <krb5_repository.h>
96*7c478bd9Sstevel@tonic-gate #include <des/des.h>
97*7c478bd9Sstevel@tonic-gate #include <rpc/des_crypt.h>
98*7c478bd9Sstevel@tonic-gate #include <sys/cryptmod.h>
99*7c478bd9Sstevel@tonic-gate #include <bsm/adt.h>
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate #define	TELNETD_OPTS "Ss:a:dEXUhR:M:"
102*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
103*7c478bd9Sstevel@tonic-gate #define	DEBUG_OPTS "p:e"
104*7c478bd9Sstevel@tonic-gate #else
105*7c478bd9Sstevel@tonic-gate #define	DEBUG_OPTS ""
106*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate #define	OPT_NO			0		/* won't do this option */
109*7c478bd9Sstevel@tonic-gate #define	OPT_YES			1		/* will do this option */
110*7c478bd9Sstevel@tonic-gate #define	OPT_YES_BUT_ALWAYS_LOOK	2
111*7c478bd9Sstevel@tonic-gate #define	OPT_NO_BUT_ALWAYS_LOOK	3
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate #define	MAXOPTLEN 256
114*7c478bd9Sstevel@tonic-gate #define	MAXUSERNAMELEN 256
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate static char	remopts[MAXOPTLEN];
117*7c478bd9Sstevel@tonic-gate static char	myopts[MAXOPTLEN];
118*7c478bd9Sstevel@tonic-gate static uchar_t	doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
119*7c478bd9Sstevel@tonic-gate static uchar_t	dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
120*7c478bd9Sstevel@tonic-gate static uchar_t	will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
121*7c478bd9Sstevel@tonic-gate static uchar_t	wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * I/O data buffers, pointers, and counters.
124*7c478bd9Sstevel@tonic-gate  */
125*7c478bd9Sstevel@tonic-gate static char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate static char	*netibuf, *netip;
128*7c478bd9Sstevel@tonic-gate static int	netibufsize;
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate #define	NIACCUM(c)	{   *netip++ = c; \
131*7c478bd9Sstevel@tonic-gate 			    ncc++; \
132*7c478bd9Sstevel@tonic-gate 			}
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate static char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
135*7c478bd9Sstevel@tonic-gate static char	*neturg = 0;		/* one past last bye of urgent data */
136*7c478bd9Sstevel@tonic-gate /* the remote system seems to NOT be an old 4.2 */
137*7c478bd9Sstevel@tonic-gate static int	not42 = 1;
138*7c478bd9Sstevel@tonic-gate static char	defaultfile[] = "/etc/default/telnetd";
139*7c478bd9Sstevel@tonic-gate static char	bannervar[] = "BANNER=";
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate static char BANNER1[] = "\r\n\r\n";
142*7c478bd9Sstevel@tonic-gate static char BANNER2[] = "\r\n\r\0\r\n\r\0";
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate /*
145*7c478bd9Sstevel@tonic-gate  * buffer for sub-options - enlarged to 4096 to handle credentials
146*7c478bd9Sstevel@tonic-gate  * from AUTH options
147*7c478bd9Sstevel@tonic-gate  */
148*7c478bd9Sstevel@tonic-gate static char	subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
149*7c478bd9Sstevel@tonic-gate #define	SB_CLEAR()	subpointer = subbuffer;
150*7c478bd9Sstevel@tonic-gate #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
151*7c478bd9Sstevel@tonic-gate #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof (subbuffer))) { \
152*7c478bd9Sstevel@tonic-gate 				*subpointer++ = (c); \
153*7c478bd9Sstevel@tonic-gate 			}
154*7c478bd9Sstevel@tonic-gate #define	SB_GET()	((*subpointer++)&0xff)
155*7c478bd9Sstevel@tonic-gate #define	SB_EOF()	(subpointer >= subend)
156*7c478bd9Sstevel@tonic-gate #define	SB_LEN()	(subend - subpointer)
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate #define	MAXCCACHENAMELEN 36
159*7c478bd9Sstevel@tonic-gate #define	MAXERRSTRLEN 1024
160*7c478bd9Sstevel@tonic-gate #define	MAXPRINCLEN 256
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate static boolean_t auth_debug = 0;
163*7c478bd9Sstevel@tonic-gate static boolean_t negotiate_auth_krb5 = 1;
164*7c478bd9Sstevel@tonic-gate static boolean_t auth_negotiated = 0;
165*7c478bd9Sstevel@tonic-gate static int auth_status = 0;
166*7c478bd9Sstevel@tonic-gate static int auth_level = 0;
167*7c478bd9Sstevel@tonic-gate static char	*AuthenticatingUser = NULL;
168*7c478bd9Sstevel@tonic-gate static char	*krb5_name = NULL;
169*7c478bd9Sstevel@tonic-gate 
170*7c478bd9Sstevel@tonic-gate static krb5_address rsaddr = { 0, 0, 0, NULL };
171*7c478bd9Sstevel@tonic-gate static krb5_address rsport = { 0, 0, 0, NULL };
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate static krb5_context telnet_context = 0;
174*7c478bd9Sstevel@tonic-gate static krb5_auth_context auth_context = 0;
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /* telnetd gets session key from here */
177*7c478bd9Sstevel@tonic-gate static krb5_ticket *ticket = NULL;
178*7c478bd9Sstevel@tonic-gate static krb5_keyblock *session_key = NULL;
179*7c478bd9Sstevel@tonic-gate static char *telnet_srvtab = NULL;
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate typedef struct {
182*7c478bd9Sstevel@tonic-gate 	uchar_t AuthName;
183*7c478bd9Sstevel@tonic-gate 	uchar_t AuthHow;
184*7c478bd9Sstevel@tonic-gate 	char  *AuthString;
185*7c478bd9Sstevel@tonic-gate } AuthInfo;
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate static AuthInfo auth_list[] = {
188*7c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
189*7c478bd9Sstevel@tonic-gate 	AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
190*7c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
191*7c478bd9Sstevel@tonic-gate 	"KRB5 MUTUAL" },
192*7c478bd9Sstevel@tonic-gate 	{AUTHTYPE_KERBEROS_V5,	AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
193*7c478bd9Sstevel@tonic-gate 	"KRB5 1-WAY" },
194*7c478bd9Sstevel@tonic-gate 	{0, 0, "NONE"}
195*7c478bd9Sstevel@tonic-gate };
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate static AuthInfo NoAuth = {0, 0, NULL};
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate static AuthInfo *authenticated = NULL;
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate #define	PREAMBLE_SIZE		5	/* for auth_reply_str allocation */
202*7c478bd9Sstevel@tonic-gate #define	POSTAMBLE_SIZE		5
203*7c478bd9Sstevel@tonic-gate #define	STR_DATA_LEN(len)	((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
204*7c478bd9Sstevel@tonic-gate 
205*7c478bd9Sstevel@tonic-gate static void auth_name(uchar_t *, int);
206*7c478bd9Sstevel@tonic-gate static void auth_is(uchar_t *, int);
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate #define	NO_ENCRYPTION   0x00
209*7c478bd9Sstevel@tonic-gate #define	SEND_ENCRYPTED  0x01
210*7c478bd9Sstevel@tonic-gate #define	RECV_ENCRYPTED  0x02
211*7c478bd9Sstevel@tonic-gate #define	ENCRYPT_BOTH_WAYS    (SEND_ENCRYPTED | RECV_ENCRYPTED)
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate static telnet_enc_data_t  encr_data;
214*7c478bd9Sstevel@tonic-gate static boolean_t negotiate_encrypt = B_TRUE;
215*7c478bd9Sstevel@tonic-gate static boolean_t sent_encrypt_support = B_FALSE;
216*7c478bd9Sstevel@tonic-gate static boolean_t sent_will_encrypt = B_FALSE;
217*7c478bd9Sstevel@tonic-gate static boolean_t sent_do_encrypt = B_FALSE;
218*7c478bd9Sstevel@tonic-gate static boolean_t enc_debug = 0;
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
221*7c478bd9Sstevel@tonic-gate static int  encrypt_send_encrypt_is();
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate extern void mit_des_fixup_key_parity(Block);
224*7c478bd9Sstevel@tonic-gate extern int krb5_setenv(const char *, const char *, int);
225*7c478bd9Sstevel@tonic-gate /* need to know what FD to use to talk to the crypto module */
226*7c478bd9Sstevel@tonic-gate static int cryptmod_fd = -1;
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate #define	LOGIN_PROGRAM "/bin/login"
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate /*
231*7c478bd9Sstevel@tonic-gate  * State for recv fsm
232*7c478bd9Sstevel@tonic-gate  */
233*7c478bd9Sstevel@tonic-gate #define	TS_DATA		0	/* base state */
234*7c478bd9Sstevel@tonic-gate #define	TS_IAC		1	/* look for double IAC's */
235*7c478bd9Sstevel@tonic-gate #define	TS_CR		2	/* CR-LF ->'s CR */
236*7c478bd9Sstevel@tonic-gate #define	TS_SB		3	/* throw away begin's... */
237*7c478bd9Sstevel@tonic-gate #define	TS_SE		4	/* ...end's (suboption negotiation) */
238*7c478bd9Sstevel@tonic-gate #define	TS_WILL		5	/* will option negotiation */
239*7c478bd9Sstevel@tonic-gate #define	TS_WONT		6	/* wont " */
240*7c478bd9Sstevel@tonic-gate #define	TS_DO		7	/* do " */
241*7c478bd9Sstevel@tonic-gate #define	TS_DONT		8	/* dont " */
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate static int	ncc;
244*7c478bd9Sstevel@tonic-gate static int	master;		/* master side of pty */
245*7c478bd9Sstevel@tonic-gate static int	pty;		/* side of pty that gets ioctls */
246*7c478bd9Sstevel@tonic-gate static int	net;
247*7c478bd9Sstevel@tonic-gate static int	inter;
248*7c478bd9Sstevel@tonic-gate extern char **environ;
249*7c478bd9Sstevel@tonic-gate static char	*line;
250*7c478bd9Sstevel@tonic-gate static int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
251*7c478bd9Sstevel@tonic-gate static int	state = TS_DATA;
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate static int env_ovar = -1;	/* XXX.sparker */
254*7c478bd9Sstevel@tonic-gate static int env_ovalue = -1;	/* XXX.sparker */
255*7c478bd9Sstevel@tonic-gate static char pam_svc_name[64];
256*7c478bd9Sstevel@tonic-gate static boolean_t	telmod_init_done = B_FALSE;
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate static void	doit(int, struct sockaddr_storage *);
259*7c478bd9Sstevel@tonic-gate static void	willoption(int);
260*7c478bd9Sstevel@tonic-gate static void	wontoption(int);
261*7c478bd9Sstevel@tonic-gate static void	dooption(int);
262*7c478bd9Sstevel@tonic-gate static void	dontoption(int);
263*7c478bd9Sstevel@tonic-gate static void	fatal(int, char *);
264*7c478bd9Sstevel@tonic-gate static void	fatalperror(int, char *, int);
265*7c478bd9Sstevel@tonic-gate static void	mode(int, int);
266*7c478bd9Sstevel@tonic-gate static void	interrupt(void);
267*7c478bd9Sstevel@tonic-gate static void	drainstream(int);
268*7c478bd9Sstevel@tonic-gate static int	readstream(int, char *, int);
269*7c478bd9Sstevel@tonic-gate static int	send_oob(int fd, char *ptr, int count);
270*7c478bd9Sstevel@tonic-gate static int	local_setenv(const char *name, const char *value, int rewrite);
271*7c478bd9Sstevel@tonic-gate static void	local_unsetenv(const char *name);
272*7c478bd9Sstevel@tonic-gate static void	suboption(void);
273*7c478bd9Sstevel@tonic-gate static int	removemod(int f, char *modname);
274*7c478bd9Sstevel@tonic-gate static void	willoption(int option);
275*7c478bd9Sstevel@tonic-gate static void	wontoption(int option);
276*7c478bd9Sstevel@tonic-gate static void	dooption(int option);
277*7c478bd9Sstevel@tonic-gate static void	dontoption(int option);
278*7c478bd9Sstevel@tonic-gate static void	write_data(const char *, ...);
279*7c478bd9Sstevel@tonic-gate static void	write_data_len(const char *, int);
280*7c478bd9Sstevel@tonic-gate static void	rmut(void);
281*7c478bd9Sstevel@tonic-gate static void	cleanup(int);
282*7c478bd9Sstevel@tonic-gate static void	telnet(int, int);
283*7c478bd9Sstevel@tonic-gate static void	telrcv(void);
284*7c478bd9Sstevel@tonic-gate static void	sendbrk(void);
285*7c478bd9Sstevel@tonic-gate static void	ptyflush(void);
286*7c478bd9Sstevel@tonic-gate static void	netclear(void);
287*7c478bd9Sstevel@tonic-gate static void	netflush(void);
288*7c478bd9Sstevel@tonic-gate static void	showbanner(void);
289*7c478bd9Sstevel@tonic-gate static void	map_banner(char *);
290*7c478bd9Sstevel@tonic-gate static void	defbanner(void);
291*7c478bd9Sstevel@tonic-gate static void ttloop(void);
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate /*
294*7c478bd9Sstevel@tonic-gate  * The env_list linked list is used to store the environment variables
295*7c478bd9Sstevel@tonic-gate  * until the final exec of login.  A malevolent client might try to
296*7c478bd9Sstevel@tonic-gate  * send an environment variable intended to affect the telnet daemon's
297*7c478bd9Sstevel@tonic-gate  * execution.  Right now the BANNER expansion is the only instance.
298*7c478bd9Sstevel@tonic-gate  * Note that it is okay to pass the environment variables to login
299*7c478bd9Sstevel@tonic-gate  * because login protects itself against environment variables mischief.
300*7c478bd9Sstevel@tonic-gate  */
301*7c478bd9Sstevel@tonic-gate 
302*7c478bd9Sstevel@tonic-gate struct envlist {
303*7c478bd9Sstevel@tonic-gate 	struct envlist	*next;
304*7c478bd9Sstevel@tonic-gate 	char		*name;
305*7c478bd9Sstevel@tonic-gate 	char		*value;
306*7c478bd9Sstevel@tonic-gate 	int		delete;
307*7c478bd9Sstevel@tonic-gate };
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate static struct envlist *envlist_head = NULL;
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate /*
312*7c478bd9Sstevel@tonic-gate  * The following are some clocks used to decide how to interpret
313*7c478bd9Sstevel@tonic-gate  * the relationship between various variables.
314*7c478bd9Sstevel@tonic-gate  */
315*7c478bd9Sstevel@tonic-gate 
316*7c478bd9Sstevel@tonic-gate static struct {
317*7c478bd9Sstevel@tonic-gate 	int
318*7c478bd9Sstevel@tonic-gate 	system,			/* what the current time is */
319*7c478bd9Sstevel@tonic-gate 	echotoggle,		/* last time user entered echo character */
320*7c478bd9Sstevel@tonic-gate 	modenegotiated,		/* last time operating mode negotiated */
321*7c478bd9Sstevel@tonic-gate 	didnetreceive,		/* last time we read data from network */
322*7c478bd9Sstevel@tonic-gate 	ttypeopt,		/* ttype will/won't received */
323*7c478bd9Sstevel@tonic-gate 	ttypesubopt,		/* ttype subopt is received */
324*7c478bd9Sstevel@tonic-gate 	getterminal,		/* time started to get terminal information */
325*7c478bd9Sstevel@tonic-gate 	xdisplocopt,		/* xdisploc will/wont received */
326*7c478bd9Sstevel@tonic-gate 	xdisplocsubopt,		/* xdisploc suboption received */
327*7c478bd9Sstevel@tonic-gate 	nawsopt,		/* window size will/wont received */
328*7c478bd9Sstevel@tonic-gate 	nawssubopt,		/* window size received */
329*7c478bd9Sstevel@tonic-gate 	environopt,		/* environment option will/wont received */
330*7c478bd9Sstevel@tonic-gate 	oenvironopt,		/* "old" environ option will/wont received */
331*7c478bd9Sstevel@tonic-gate 	environsubopt,		/* environment option suboption received */
332*7c478bd9Sstevel@tonic-gate 	oenvironsubopt,		/* "old environ option suboption received */
333*7c478bd9Sstevel@tonic-gate 	gotDM;			/* when did we last see a data mark */
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 	int getauth;
336*7c478bd9Sstevel@tonic-gate 	int authopt;	/* Authentication option negotiated */
337*7c478bd9Sstevel@tonic-gate 	int authdone;
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 	int getencr;
340*7c478bd9Sstevel@tonic-gate 	int encropt;
341*7c478bd9Sstevel@tonic-gate 	int encr_support;
342*7c478bd9Sstevel@tonic-gate } clocks;
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate static int init_neg_done = 0;
345*7c478bd9Sstevel@tonic-gate static boolean_t resolve_hostname = 0;
346*7c478bd9Sstevel@tonic-gate static boolean_t show_hostinfo = 1;
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate #define	settimer(x)	(clocks.x = ++clocks.system)
349*7c478bd9Sstevel@tonic-gate #define	sequenceIs(x, y)	(clocks.x < clocks.y)
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate static void send_will(int);
352*7c478bd9Sstevel@tonic-gate static void send_wont(int);
353*7c478bd9Sstevel@tonic-gate static void send_do(int);
354*7c478bd9Sstevel@tonic-gate static char *__findenv(const char *name, int *offset);
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
357*7c478bd9Sstevel@tonic-gate static void
358*7c478bd9Sstevel@tonic-gate auth_finished(AuthInfo *ap, int result)
359*7c478bd9Sstevel@tonic-gate {
360*7c478bd9Sstevel@tonic-gate 	if ((authenticated = ap) == NULL) {
361*7c478bd9Sstevel@tonic-gate 		authenticated = &NoAuth;
362*7c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ENCRYPT] == OPT_YES)
363*7c478bd9Sstevel@tonic-gate 			send_wont(TELOPT_ENCRYPT);
364*7c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
365*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 0;
366*7c478bd9Sstevel@tonic-gate 	} else if (result != AUTH_REJECT &&
367*7c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] == OPT_YES &&
368*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] == OPT_YES) {
369*7c478bd9Sstevel@tonic-gate 
370*7c478bd9Sstevel@tonic-gate 		/*
371*7c478bd9Sstevel@tonic-gate 		 * Authentication successful, so we have a session key, and
372*7c478bd9Sstevel@tonic-gate 		 * we're willing to do ENCRYPT, so send our ENCRYPT SUPPORT.
373*7c478bd9Sstevel@tonic-gate 		 *
374*7c478bd9Sstevel@tonic-gate 		 * Can't have sent ENCRYPT SUPPORT yet!  And if we're sending it
375*7c478bd9Sstevel@tonic-gate 		 * now it's really only because we did the DO ENCRYPT/WILL
376*7c478bd9Sstevel@tonic-gate 		 * ENCRYPT dance before authentication, which is ok, but not too
377*7c478bd9Sstevel@tonic-gate 		 * bright since we have to do the DONT ENCRYPT/WONT ENCRYPT
378*7c478bd9Sstevel@tonic-gate 		 * dance if authentication fails, though clients typically just
379*7c478bd9Sstevel@tonic-gate 		 * don't care.
380*7c478bd9Sstevel@tonic-gate 		 */
381*7c478bd9Sstevel@tonic-gate 		write_data("%c%c%c%c%c%c%c",
382*7c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
383*7c478bd9Sstevel@tonic-gate 			(uchar_t)SB,
384*7c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCRYPT,
385*7c478bd9Sstevel@tonic-gate 			(uchar_t)ENCRYPT_SUPPORT,
386*7c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
387*7c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
388*7c478bd9Sstevel@tonic-gate 			(uchar_t)SE);
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 		netflush();
391*7c478bd9Sstevel@tonic-gate 
392*7c478bd9Sstevel@tonic-gate 		sent_encrypt_support = B_TRUE;
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
395*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
396*7c478bd9Sstevel@tonic-gate 			"SENT ENCRYPT SUPPORT\n");
397*7c478bd9Sstevel@tonic-gate 
398*7c478bd9Sstevel@tonic-gate 		(void) encrypt_send_encrypt_is();
399*7c478bd9Sstevel@tonic-gate 	}
400*7c478bd9Sstevel@tonic-gate 
401*7c478bd9Sstevel@tonic-gate 	auth_status = result;
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	settimer(authdone);
404*7c478bd9Sstevel@tonic-gate }
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate static void
407*7c478bd9Sstevel@tonic-gate reply_to_client(AuthInfo *ap, int type, void *data, int len)
408*7c478bd9Sstevel@tonic-gate {
409*7c478bd9Sstevel@tonic-gate 	uchar_t reply[BUFSIZ];
410*7c478bd9Sstevel@tonic-gate 	uchar_t *p = reply;
411*7c478bd9Sstevel@tonic-gate 	uchar_t *cd = (uchar_t *)data;
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 	if (len == -1 && data != NULL)
414*7c478bd9Sstevel@tonic-gate 		len = strlen((char *)data);
415*7c478bd9Sstevel@tonic-gate 	else if (len > (sizeof (reply) - 9)) {
416*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
417*7c478bd9Sstevel@tonic-gate 		    "krb5 auth reply length too large (%d)", len);
418*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
419*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
420*7c478bd9Sstevel@tonic-gate 				    "krb5 auth reply length too large (%d)\n",
421*7c478bd9Sstevel@tonic-gate 				    len);
422*7c478bd9Sstevel@tonic-gate 		return;
423*7c478bd9Sstevel@tonic-gate 	} else if (data == NULL)
424*7c478bd9Sstevel@tonic-gate 		len = 0;
425*7c478bd9Sstevel@tonic-gate 
426*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
427*7c478bd9Sstevel@tonic-gate 	*p++ = SB;
428*7c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_AUTHENTICATION;
429*7c478bd9Sstevel@tonic-gate 	*p++ = AUTHTYPE_KERBEROS_V5;
430*7c478bd9Sstevel@tonic-gate 	*p++ = ap->AuthName;
431*7c478bd9Sstevel@tonic-gate 	*p++ = ap->AuthHow; /* MUTUAL, ONE-WAY, etc */
432*7c478bd9Sstevel@tonic-gate 	*p++ = type;	    /* RESPONSE or ACCEPT */
433*7c478bd9Sstevel@tonic-gate 	while (len-- > 0) {
434*7c478bd9Sstevel@tonic-gate 		if ((*p++ = *cd++) == IAC)
435*7c478bd9Sstevel@tonic-gate 			*p++ = IAC;
436*7c478bd9Sstevel@tonic-gate 	}
437*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
438*7c478bd9Sstevel@tonic-gate 	*p++ = SE;
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate 	/* queue the data to be sent */
441*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)reply, p-reply);
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate #if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
444*7c478bd9Sstevel@tonic-gate defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
445*7c478bd9Sstevel@tonic-gate 	if (auth_debug) {
446*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
447*7c478bd9Sstevel@tonic-gate 			    "%s %s|%s %s\n",
448*7c478bd9Sstevel@tonic-gate 			    AUTHTYPE_NAME(ap->AuthName),
449*7c478bd9Sstevel@tonic-gate 			    AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
450*7c478bd9Sstevel@tonic-gate 			    AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
451*7c478bd9Sstevel@tonic-gate 			    AUTHRSP_NAME(type));
452*7c478bd9Sstevel@tonic-gate 	}
453*7c478bd9Sstevel@tonic-gate #endif /* AUTHTYPE_NAMES && AUTHWHO_NAMES && AUTHHOW_NAMES && AUTHRSP_NAMES */
454*7c478bd9Sstevel@tonic-gate 
455*7c478bd9Sstevel@tonic-gate 	netflush();
456*7c478bd9Sstevel@tonic-gate }
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate /* Decode, decrypt and store the forwarded creds in the local ccache. */
459*7c478bd9Sstevel@tonic-gate static krb5_error_code
460*7c478bd9Sstevel@tonic-gate rd_and_store_forwarded_creds(krb5_context context,
461*7c478bd9Sstevel@tonic-gate 			    krb5_auth_context auth_context,
462*7c478bd9Sstevel@tonic-gate 			    krb5_data *inbuf, krb5_ticket *ticket,
463*7c478bd9Sstevel@tonic-gate 			    char *username)
464*7c478bd9Sstevel@tonic-gate {
465*7c478bd9Sstevel@tonic-gate 	krb5_creds **creds;
466*7c478bd9Sstevel@tonic-gate 	krb5_error_code retval;
467*7c478bd9Sstevel@tonic-gate 	char ccname[MAXCCACHENAMELEN];
468*7c478bd9Sstevel@tonic-gate 	krb5_ccache ccache = NULL;
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate 	if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
471*7c478bd9Sstevel@tonic-gate 		return (retval);
472*7c478bd9Sstevel@tonic-gate 
473*7c478bd9Sstevel@tonic-gate 	(void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
474*7c478bd9Sstevel@tonic-gate 	(void) krb5_setenv("KRB5CCNAME", ccname, 1);
475*7c478bd9Sstevel@tonic-gate 
476*7c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_default(context, &ccache)))
477*7c478bd9Sstevel@tonic-gate 		goto cleanup;
478*7c478bd9Sstevel@tonic-gate 
479*7c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_initialize(context, ccache,
480*7c478bd9Sstevel@tonic-gate 					ticket->enc_part2->client)) != 0)
481*7c478bd9Sstevel@tonic-gate 		goto cleanup;
482*7c478bd9Sstevel@tonic-gate 
483*7c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
484*7c478bd9Sstevel@tonic-gate 		goto cleanup;
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate 	if ((retval = krb5_cc_close(context, ccache)) != 0)
487*7c478bd9Sstevel@tonic-gate 		goto cleanup;
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 	if (username != NULL) {
490*7c478bd9Sstevel@tonic-gate 		/*
491*7c478bd9Sstevel@tonic-gate 		 * This verifies that the user is valid on the local system,
492*7c478bd9Sstevel@tonic-gate 		 * maps the username from KerberosV5 to unix,
493*7c478bd9Sstevel@tonic-gate 		 * and moves the KRB5CCNAME file to the correct place
494*7c478bd9Sstevel@tonic-gate 		 *  /tmp/krb5cc_[uid] with correct ownership (0600 uid gid).
495*7c478bd9Sstevel@tonic-gate 		 *
496*7c478bd9Sstevel@tonic-gate 		 * NOTE: the user must be in the gsscred table in order to map
497*7c478bd9Sstevel@tonic-gate 		 * from KRB5 to Unix.
498*7c478bd9Sstevel@tonic-gate 		 */
499*7c478bd9Sstevel@tonic-gate 		(void) krb5_kuserok(context, ticket->enc_part2->client,
500*7c478bd9Sstevel@tonic-gate 				username);
501*7c478bd9Sstevel@tonic-gate 	}
502*7c478bd9Sstevel@tonic-gate 	if (auth_debug)
503*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
504*7c478bd9Sstevel@tonic-gate 			    "Successfully stored forwarded creds\n");
505*7c478bd9Sstevel@tonic-gate 
506*7c478bd9Sstevel@tonic-gate cleanup:
507*7c478bd9Sstevel@tonic-gate 	krb5_free_creds(context, *creds);
508*7c478bd9Sstevel@tonic-gate 	return (retval);
509*7c478bd9Sstevel@tonic-gate }
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate static void
512*7c478bd9Sstevel@tonic-gate kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
513*7c478bd9Sstevel@tonic-gate {
514*7c478bd9Sstevel@tonic-gate 	krb5_error_code err = 0;
515*7c478bd9Sstevel@tonic-gate 	krb5_principal server;
516*7c478bd9Sstevel@tonic-gate 	krb5_keyblock *newkey = NULL;
517*7c478bd9Sstevel@tonic-gate 	krb5_keytab keytabid = 0;
518*7c478bd9Sstevel@tonic-gate 	krb5_data outbuf;
519*7c478bd9Sstevel@tonic-gate 	krb5_data inbuf;
520*7c478bd9Sstevel@tonic-gate 	krb5_authenticator *authenticator;
521*7c478bd9Sstevel@tonic-gate 	char errbuf[MAXERRSTRLEN];
522*7c478bd9Sstevel@tonic-gate 	char *name;
523*7c478bd9Sstevel@tonic-gate 	krb5_data auth;
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	Session_Key skey;
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate 	if (cnt-- < 1)
528*7c478bd9Sstevel@tonic-gate 		return;
529*7c478bd9Sstevel@tonic-gate 	switch (*data++) {
530*7c478bd9Sstevel@tonic-gate 	case KRB_AUTH:
531*7c478bd9Sstevel@tonic-gate 		auth.data = (char *)data;
532*7c478bd9Sstevel@tonic-gate 		auth.length = cnt;
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 		if (auth_context == NULL) {
535*7c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_init(telnet_context, &auth_context);
536*7c478bd9Sstevel@tonic-gate 			if (err)
537*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
538*7c478bd9Sstevel@tonic-gate 				    "Error getting krb5 auth "
539*7c478bd9Sstevel@tonic-gate 				    "context: %s", error_message(err));
540*7c478bd9Sstevel@tonic-gate 		}
541*7c478bd9Sstevel@tonic-gate 		if (!err) {
542*7c478bd9Sstevel@tonic-gate 			krb5_rcache rcache;
543*7c478bd9Sstevel@tonic-gate 
544*7c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getrcache(telnet_context,
545*7c478bd9Sstevel@tonic-gate 						    auth_context,
546*7c478bd9Sstevel@tonic-gate 						    &rcache);
547*7c478bd9Sstevel@tonic-gate 			if (!err && !rcache) {
548*7c478bd9Sstevel@tonic-gate 				err = krb5_sname_to_principal(telnet_context,
549*7c478bd9Sstevel@tonic-gate 							    0, 0,
550*7c478bd9Sstevel@tonic-gate 							    KRB5_NT_SRV_HST,
551*7c478bd9Sstevel@tonic-gate 							    &server);
552*7c478bd9Sstevel@tonic-gate 				if (!err) {
553*7c478bd9Sstevel@tonic-gate 					err = krb5_get_server_rcache(
554*7c478bd9Sstevel@tonic-gate 						telnet_context,
555*7c478bd9Sstevel@tonic-gate 						krb5_princ_component(
556*7c478bd9Sstevel@tonic-gate 							telnet_context,
557*7c478bd9Sstevel@tonic-gate 							server, 0),
558*7c478bd9Sstevel@tonic-gate 						&rcache);
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate 					krb5_free_principal(telnet_context,
561*7c478bd9Sstevel@tonic-gate 							    server);
562*7c478bd9Sstevel@tonic-gate 				}
563*7c478bd9Sstevel@tonic-gate 			}
564*7c478bd9Sstevel@tonic-gate 			if (err)
565*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
566*7c478bd9Sstevel@tonic-gate 				    "Error allocating krb5 replay cache: %s",
567*7c478bd9Sstevel@tonic-gate 				    error_message(err));
568*7c478bd9Sstevel@tonic-gate 			else {
569*7c478bd9Sstevel@tonic-gate 				err = krb5_auth_con_setrcache(telnet_context,
570*7c478bd9Sstevel@tonic-gate 							    auth_context,
571*7c478bd9Sstevel@tonic-gate 							    rcache);
572*7c478bd9Sstevel@tonic-gate 				if (err)
573*7c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
574*7c478bd9Sstevel@tonic-gate 					    "Error creating krb5 "
575*7c478bd9Sstevel@tonic-gate 					    "replay cache: %s",
576*7c478bd9Sstevel@tonic-gate 					    error_message(err));
577*7c478bd9Sstevel@tonic-gate 			}
578*7c478bd9Sstevel@tonic-gate 		}
579*7c478bd9Sstevel@tonic-gate 		if (!err && telnet_srvtab != NULL)
580*7c478bd9Sstevel@tonic-gate 			err = krb5_kt_resolve(telnet_context,
581*7c478bd9Sstevel@tonic-gate 					    telnet_srvtab, &keytabid);
582*7c478bd9Sstevel@tonic-gate 		if (!err)
583*7c478bd9Sstevel@tonic-gate 			err = krb5_rd_req(telnet_context, &auth_context, &auth,
584*7c478bd9Sstevel@tonic-gate 					NULL, keytabid, NULL, &ticket);
585*7c478bd9Sstevel@tonic-gate 		if (err) {
586*7c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
587*7c478bd9Sstevel@tonic-gate 				"Error reading krb5 auth information:"
588*7c478bd9Sstevel@tonic-gate 				" %s", error_message(err));
589*7c478bd9Sstevel@tonic-gate 			goto errout;
590*7c478bd9Sstevel@tonic-gate 		}
591*7c478bd9Sstevel@tonic-gate 
592*7c478bd9Sstevel@tonic-gate 		/*
593*7c478bd9Sstevel@tonic-gate 		 * Verify that the correct principal was used
594*7c478bd9Sstevel@tonic-gate 		 */
595*7c478bd9Sstevel@tonic-gate 		if (krb5_princ_component(telnet_context,
596*7c478bd9Sstevel@tonic-gate 				ticket->server, 0)->length < MAXPRINCLEN) {
597*7c478bd9Sstevel@tonic-gate 			char princ[MAXPRINCLEN];
598*7c478bd9Sstevel@tonic-gate 			(void) strncpy(princ,
599*7c478bd9Sstevel@tonic-gate 				    krb5_princ_component(telnet_context,
600*7c478bd9Sstevel@tonic-gate 						ticket->server, 0)->data,
601*7c478bd9Sstevel@tonic-gate 				    krb5_princ_component(telnet_context,
602*7c478bd9Sstevel@tonic-gate 					    ticket->server, 0)->length);
603*7c478bd9Sstevel@tonic-gate 			princ[krb5_princ_component(telnet_context,
604*7c478bd9Sstevel@tonic-gate 					ticket->server, 0)->length] = '\0';
605*7c478bd9Sstevel@tonic-gate 			if (strcmp("host", princ)) {
606*7c478bd9Sstevel@tonic-gate 				if (strlen(princ) < sizeof (errbuf) - 39) {
607*7c478bd9Sstevel@tonic-gate 				    (void) snprintf(errbuf, sizeof (errbuf),
608*7c478bd9Sstevel@tonic-gate 						"incorrect service "
609*7c478bd9Sstevel@tonic-gate 						    "name: \"%s\" != "
610*7c478bd9Sstevel@tonic-gate 						    "\"host\"",
611*7c478bd9Sstevel@tonic-gate 						    princ);
612*7c478bd9Sstevel@tonic-gate 			    } else {
613*7c478bd9Sstevel@tonic-gate 				    (void) strncpy(errbuf,
614*7c478bd9Sstevel@tonic-gate 						"incorrect service "
615*7c478bd9Sstevel@tonic-gate 						"name: principal != "
616*7c478bd9Sstevel@tonic-gate 						"\"host\"",
617*7c478bd9Sstevel@tonic-gate 						sizeof (errbuf));
618*7c478bd9Sstevel@tonic-gate 			    }
619*7c478bd9Sstevel@tonic-gate 			    goto errout;
620*7c478bd9Sstevel@tonic-gate 			}
621*7c478bd9Sstevel@tonic-gate 		} else {
622*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(errbuf, "service name too long",
623*7c478bd9Sstevel@tonic-gate 					sizeof (errbuf));
624*7c478bd9Sstevel@tonic-gate 			goto errout;
625*7c478bd9Sstevel@tonic-gate 		}
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 		err = krb5_auth_con_getauthenticator(telnet_context,
628*7c478bd9Sstevel@tonic-gate 						auth_context,
629*7c478bd9Sstevel@tonic-gate 						&authenticator);
630*7c478bd9Sstevel@tonic-gate 		if (err) {
631*7c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
632*7c478bd9Sstevel@tonic-gate 				"Failed to get authenticator: %s",
633*7c478bd9Sstevel@tonic-gate 				error_message(err));
634*7c478bd9Sstevel@tonic-gate 			goto errout;
635*7c478bd9Sstevel@tonic-gate 		}
636*7c478bd9Sstevel@tonic-gate 		if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
637*7c478bd9Sstevel@tonic-gate 			!authenticator->checksum) {
638*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(errbuf,
639*7c478bd9Sstevel@tonic-gate 				    "authenticator is missing checksum",
640*7c478bd9Sstevel@tonic-gate 				    sizeof (errbuf));
641*7c478bd9Sstevel@tonic-gate 			goto errout;
642*7c478bd9Sstevel@tonic-gate 		}
643*7c478bd9Sstevel@tonic-gate 		if (authenticator->checksum) {
644*7c478bd9Sstevel@tonic-gate 			char type_check[2];
645*7c478bd9Sstevel@tonic-gate 			krb5_checksum *cksum = authenticator->checksum;
646*7c478bd9Sstevel@tonic-gate 			krb5_keyblock *key;
647*7c478bd9Sstevel@tonic-gate 			krb5_data input;
648*7c478bd9Sstevel@tonic-gate 			krb5_boolean valid;
649*7c478bd9Sstevel@tonic-gate 
650*7c478bd9Sstevel@tonic-gate 			type_check[0] = ap->AuthName;
651*7c478bd9Sstevel@tonic-gate 			type_check[1] = ap->AuthHow;
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getkey(telnet_context,
654*7c478bd9Sstevel@tonic-gate 						auth_context, &key);
655*7c478bd9Sstevel@tonic-gate 			if (err) {
656*7c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
657*7c478bd9Sstevel@tonic-gate 					"Failed to get key from "
658*7c478bd9Sstevel@tonic-gate 					"authenticator: %s",
659*7c478bd9Sstevel@tonic-gate 					error_message(err));
660*7c478bd9Sstevel@tonic-gate 				goto errout;
661*7c478bd9Sstevel@tonic-gate 			}
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 			input.data = type_check;
664*7c478bd9Sstevel@tonic-gate 			input.length = 2;
665*7c478bd9Sstevel@tonic-gate 			err = krb5_c_verify_checksum(telnet_context,
666*7c478bd9Sstevel@tonic-gate 							key, 0,
667*7c478bd9Sstevel@tonic-gate 							&input,
668*7c478bd9Sstevel@tonic-gate 							cksum,
669*7c478bd9Sstevel@tonic-gate 							&valid);
670*7c478bd9Sstevel@tonic-gate 			if (!err && !valid)
671*7c478bd9Sstevel@tonic-gate 				err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
672*7c478bd9Sstevel@tonic-gate 
673*7c478bd9Sstevel@tonic-gate 			if (err) {
674*7c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
675*7c478bd9Sstevel@tonic-gate 						"Kerberos checksum "
676*7c478bd9Sstevel@tonic-gate 						"verification failed: "
677*7c478bd9Sstevel@tonic-gate 						"%s",
678*7c478bd9Sstevel@tonic-gate 						error_message(err));
679*7c478bd9Sstevel@tonic-gate 				goto errout;
680*7c478bd9Sstevel@tonic-gate 			}
681*7c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, key);
682*7c478bd9Sstevel@tonic-gate 		}
683*7c478bd9Sstevel@tonic-gate 
684*7c478bd9Sstevel@tonic-gate 		krb5_free_authenticator(telnet_context, authenticator);
685*7c478bd9Sstevel@tonic-gate 		if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
686*7c478bd9Sstevel@tonic-gate 			/* do ap_rep stuff here */
687*7c478bd9Sstevel@tonic-gate 			if ((err = krb5_mk_rep(telnet_context, auth_context,
688*7c478bd9Sstevel@tonic-gate 					    &outbuf))) {
689*7c478bd9Sstevel@tonic-gate 				(void) snprintf(errbuf, sizeof (errbuf),
690*7c478bd9Sstevel@tonic-gate 						"Failed to make "
691*7c478bd9Sstevel@tonic-gate 						"Kerberos auth reply: "
692*7c478bd9Sstevel@tonic-gate 						"%s",
693*7c478bd9Sstevel@tonic-gate 						error_message(err));
694*7c478bd9Sstevel@tonic-gate 				goto errout;
695*7c478bd9Sstevel@tonic-gate 			}
696*7c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_RESPONSE, outbuf.data,
697*7c478bd9Sstevel@tonic-gate 					outbuf.length);
698*7c478bd9Sstevel@tonic-gate 		}
699*7c478bd9Sstevel@tonic-gate 		if (krb5_unparse_name(telnet_context,
700*7c478bd9Sstevel@tonic-gate 				    ticket->enc_part2->client,
701*7c478bd9Sstevel@tonic-gate 				    &name))
702*7c478bd9Sstevel@tonic-gate 			name = 0;
703*7c478bd9Sstevel@tonic-gate 		reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
704*7c478bd9Sstevel@tonic-gate 		if (auth_debug) {
705*7c478bd9Sstevel@tonic-gate 			syslog(LOG_NOTICE,
706*7c478bd9Sstevel@tonic-gate 			    "\tKerberos5 identifies user as ``%s''\r\n",
707*7c478bd9Sstevel@tonic-gate 			    name ? name : "");
708*7c478bd9Sstevel@tonic-gate 		}
709*7c478bd9Sstevel@tonic-gate 		if (name != NULL) {
710*7c478bd9Sstevel@tonic-gate 			krb5_name = (char *)strdup(name);
711*7c478bd9Sstevel@tonic-gate 		}
712*7c478bd9Sstevel@tonic-gate 		auth_finished(ap, AUTH_USER);
713*7c478bd9Sstevel@tonic-gate 
714*7c478bd9Sstevel@tonic-gate 		if (name != NULL)
715*7c478bd9Sstevel@tonic-gate 			free(name);
716*7c478bd9Sstevel@tonic-gate 		krb5_auth_con_getremotesubkey(telnet_context, auth_context,
717*7c478bd9Sstevel@tonic-gate 					    &newkey);
718*7c478bd9Sstevel@tonic-gate 		if (session_key != NULL) {
719*7c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, session_key);
720*7c478bd9Sstevel@tonic-gate 			session_key = 0;
721*7c478bd9Sstevel@tonic-gate 		}
722*7c478bd9Sstevel@tonic-gate 		if (newkey != NULL) {
723*7c478bd9Sstevel@tonic-gate 			krb5_copy_keyblock(telnet_context,
724*7c478bd9Sstevel@tonic-gate 					newkey, &session_key);
725*7c478bd9Sstevel@tonic-gate 			krb5_free_keyblock(telnet_context, newkey);
726*7c478bd9Sstevel@tonic-gate 		} else {
727*7c478bd9Sstevel@tonic-gate 			krb5_copy_keyblock(telnet_context,
728*7c478bd9Sstevel@tonic-gate 				ticket->enc_part2->session,
729*7c478bd9Sstevel@tonic-gate 				&session_key);
730*7c478bd9Sstevel@tonic-gate 		}
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 		/*
733*7c478bd9Sstevel@tonic-gate 		 * Initialize encryption stuff.  Currently, we are only
734*7c478bd9Sstevel@tonic-gate 		 * supporting 8 byte keys and blocks. Check for this later.
735*7c478bd9Sstevel@tonic-gate 		 */
736*7c478bd9Sstevel@tonic-gate 		skey.type = SK_DES;
737*7c478bd9Sstevel@tonic-gate 		skey.length = DES_BLOCKSIZE;
738*7c478bd9Sstevel@tonic-gate 		skey.data = session_key->contents;
739*7c478bd9Sstevel@tonic-gate 		encrypt_session_key(&skey, &encr_data.encrypt);
740*7c478bd9Sstevel@tonic-gate 		encrypt_session_key(&skey, &encr_data.decrypt);
741*7c478bd9Sstevel@tonic-gate 		break;
742*7c478bd9Sstevel@tonic-gate 	case KRB_FORWARD:
743*7c478bd9Sstevel@tonic-gate 		inbuf.length = cnt;
744*7c478bd9Sstevel@tonic-gate 		inbuf.data = (char *)data;
745*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
746*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
747*7c478bd9Sstevel@tonic-gate 				    "RCVD KRB_FORWARD data (%d bytes)\n", cnt);
748*7c478bd9Sstevel@tonic-gate 
749*7c478bd9Sstevel@tonic-gate 		if (auth_context != NULL) {
750*7c478bd9Sstevel@tonic-gate 			krb5_rcache rcache;
751*7c478bd9Sstevel@tonic-gate 
752*7c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_getrcache(telnet_context,
753*7c478bd9Sstevel@tonic-gate 						    auth_context, &rcache);
754*7c478bd9Sstevel@tonic-gate 			if (!err && !rcache) {
755*7c478bd9Sstevel@tonic-gate 				err = krb5_sname_to_principal(telnet_context,
756*7c478bd9Sstevel@tonic-gate 					0, 0, KRB5_NT_SRV_HST, &server);
757*7c478bd9Sstevel@tonic-gate 				if (!err) {
758*7c478bd9Sstevel@tonic-gate 					err = krb5_get_server_rcache(
759*7c478bd9Sstevel@tonic-gate 						telnet_context,
760*7c478bd9Sstevel@tonic-gate 						krb5_princ_component(
761*7c478bd9Sstevel@tonic-gate 							telnet_context,
762*7c478bd9Sstevel@tonic-gate 							server, 0),
763*7c478bd9Sstevel@tonic-gate 						&rcache);
764*7c478bd9Sstevel@tonic-gate 					krb5_free_principal(telnet_context,
765*7c478bd9Sstevel@tonic-gate 								server);
766*7c478bd9Sstevel@tonic-gate 				}
767*7c478bd9Sstevel@tonic-gate 			}
768*7c478bd9Sstevel@tonic-gate 			if (err) {
769*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
770*7c478bd9Sstevel@tonic-gate 				    "Error allocating krb5 replay cache: %s",
771*7c478bd9Sstevel@tonic-gate 				    error_message(err));
772*7c478bd9Sstevel@tonic-gate 			} else {
773*7c478bd9Sstevel@tonic-gate 				err = krb5_auth_con_setrcache(telnet_context,
774*7c478bd9Sstevel@tonic-gate 					auth_context, rcache);
775*7c478bd9Sstevel@tonic-gate 				if (err)
776*7c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
777*7c478bd9Sstevel@tonic-gate 					    "Error creating krb5 replay cache:"
778*7c478bd9Sstevel@tonic-gate 					    " %s",
779*7c478bd9Sstevel@tonic-gate 					    error_message(err));
780*7c478bd9Sstevel@tonic-gate 			}
781*7c478bd9Sstevel@tonic-gate 		}
782*7c478bd9Sstevel@tonic-gate 		/*
783*7c478bd9Sstevel@tonic-gate 		 * Use the 'rsaddr' and 'rsport' (remote service addr/port)
784*7c478bd9Sstevel@tonic-gate 		 * from the original connection.  This data is used to
785*7c478bd9Sstevel@tonic-gate 		 * verify the forwarded credentials.
786*7c478bd9Sstevel@tonic-gate 		 */
787*7c478bd9Sstevel@tonic-gate 		if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
788*7c478bd9Sstevel@tonic-gate 					    NULL, &rsaddr)))
789*7c478bd9Sstevel@tonic-gate 			err = krb5_auth_con_setports(telnet_context,
790*7c478bd9Sstevel@tonic-gate 						auth_context, NULL, &rsport);
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 		if (err == 0)
793*7c478bd9Sstevel@tonic-gate 			/*
794*7c478bd9Sstevel@tonic-gate 			 * If all is well, store the forwarded creds in
795*7c478bd9Sstevel@tonic-gate 			 * the users local credential cache.
796*7c478bd9Sstevel@tonic-gate 			 */
797*7c478bd9Sstevel@tonic-gate 			err = rd_and_store_forwarded_creds(telnet_context,
798*7c478bd9Sstevel@tonic-gate 							auth_context, &inbuf,
799*7c478bd9Sstevel@tonic-gate 							ticket,
800*7c478bd9Sstevel@tonic-gate 							AuthenticatingUser);
801*7c478bd9Sstevel@tonic-gate 		if (err) {
802*7c478bd9Sstevel@tonic-gate 			(void) snprintf(errbuf, sizeof (errbuf),
803*7c478bd9Sstevel@tonic-gate 					"Read forwarded creds failed: %s",
804*7c478bd9Sstevel@tonic-gate 					error_message(err));
805*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "%s", errbuf);
806*7c478bd9Sstevel@tonic-gate 
807*7c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
808*7c478bd9Sstevel@tonic-gate 			if (auth_debug)
809*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
810*7c478bd9Sstevel@tonic-gate 					    "\tCould not read "
811*7c478bd9Sstevel@tonic-gate 					    "forwarded credentials\r\n");
812*7c478bd9Sstevel@tonic-gate 		} else
813*7c478bd9Sstevel@tonic-gate 			reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
816*7c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
819*7c478bd9Sstevel@tonic-gate 			free(rsport.contents);
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
822*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\tForwarded "
823*7c478bd9Sstevel@tonic-gate 						"credentials obtained\r\n");
824*7c478bd9Sstevel@tonic-gate 		break;
825*7c478bd9Sstevel@tonic-gate 	default:
826*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
827*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
828*7c478bd9Sstevel@tonic-gate 				    "\tUnknown Kerberos option %d\r\n",
829*7c478bd9Sstevel@tonic-gate 				    data[-1]);
830*7c478bd9Sstevel@tonic-gate 		reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
831*7c478bd9Sstevel@tonic-gate 		break;
832*7c478bd9Sstevel@tonic-gate 	}
833*7c478bd9Sstevel@tonic-gate 	return;
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate errout:
836*7c478bd9Sstevel@tonic-gate 	reply_to_client(ap, KRB_REJECT, errbuf, -1);
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	if (auth_debug)
839*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
840*7c478bd9Sstevel@tonic-gate 
841*7c478bd9Sstevel@tonic-gate 	syslog(LOG_ERR, "%s", errbuf);
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 	if (auth_context != NULL) {
844*7c478bd9Sstevel@tonic-gate 		krb5_auth_con_free(telnet_context, auth_context);
845*7c478bd9Sstevel@tonic-gate 		auth_context = 0;
846*7c478bd9Sstevel@tonic-gate 	}
847*7c478bd9Sstevel@tonic-gate }
848*7c478bd9Sstevel@tonic-gate 
849*7c478bd9Sstevel@tonic-gate static int
850*7c478bd9Sstevel@tonic-gate krb5_init()
851*7c478bd9Sstevel@tonic-gate {
852*7c478bd9Sstevel@tonic-gate 	int code = 0;
853*7c478bd9Sstevel@tonic-gate 
854*7c478bd9Sstevel@tonic-gate 	if (telnet_context == NULL) {
855*7c478bd9Sstevel@tonic-gate 		code = krb5_init_context(&telnet_context);
856*7c478bd9Sstevel@tonic-gate 		if (code != 0 && auth_debug)
857*7c478bd9Sstevel@tonic-gate 			syslog(LOG_NOTICE,
858*7c478bd9Sstevel@tonic-gate 			    "Cannot initialize Kerberos V5: %s",
859*7c478bd9Sstevel@tonic-gate 			    error_message(code));
860*7c478bd9Sstevel@tonic-gate 	}
861*7c478bd9Sstevel@tonic-gate 
862*7c478bd9Sstevel@tonic-gate 	return (code);
863*7c478bd9Sstevel@tonic-gate }
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate static void
866*7c478bd9Sstevel@tonic-gate auth_name(uchar_t *data, int cnt)
867*7c478bd9Sstevel@tonic-gate {
868*7c478bd9Sstevel@tonic-gate 	char namebuf[MAXPRINCLEN];
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate 	if (cnt < 1) {
871*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
872*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
873*7c478bd9Sstevel@tonic-gate 				    "\t(auth_name) Empty NAME in auth "
874*7c478bd9Sstevel@tonic-gate 				    "reply\n");
875*7c478bd9Sstevel@tonic-gate 		return;
876*7c478bd9Sstevel@tonic-gate 	}
877*7c478bd9Sstevel@tonic-gate 	if (cnt > sizeof (namebuf)-1) {
878*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
879*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
880*7c478bd9Sstevel@tonic-gate 				    "\t(auth_name) NAME exceeds %d bytes\n",
881*7c478bd9Sstevel@tonic-gate 				sizeof (namebuf)-1);
882*7c478bd9Sstevel@tonic-gate 		return;
883*7c478bd9Sstevel@tonic-gate 	}
884*7c478bd9Sstevel@tonic-gate 	(void) memcpy((void *)namebuf, (void *)data, cnt);
885*7c478bd9Sstevel@tonic-gate 	namebuf[cnt] = 0;
886*7c478bd9Sstevel@tonic-gate 	if (auth_debug)
887*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
888*7c478bd9Sstevel@tonic-gate 	AuthenticatingUser = (char *)strdup(namebuf);
889*7c478bd9Sstevel@tonic-gate }
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate static void
892*7c478bd9Sstevel@tonic-gate auth_is(uchar_t *data, int cnt)
893*7c478bd9Sstevel@tonic-gate {
894*7c478bd9Sstevel@tonic-gate 	AuthInfo *aptr = auth_list;
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate 	if (cnt < 2)
897*7c478bd9Sstevel@tonic-gate 		return;
898*7c478bd9Sstevel@tonic-gate 
899*7c478bd9Sstevel@tonic-gate 	/*
900*7c478bd9Sstevel@tonic-gate 	 * We failed to negoiate secure authentication
901*7c478bd9Sstevel@tonic-gate 	 */
902*7c478bd9Sstevel@tonic-gate 	if (data[0] == AUTHTYPE_NULL) {
903*7c478bd9Sstevel@tonic-gate 		auth_finished(0, AUTH_REJECT);
904*7c478bd9Sstevel@tonic-gate 		return;
905*7c478bd9Sstevel@tonic-gate 	}
906*7c478bd9Sstevel@tonic-gate 
907*7c478bd9Sstevel@tonic-gate 	while (aptr->AuthName != NULL &&
908*7c478bd9Sstevel@tonic-gate 	    (aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
909*7c478bd9Sstevel@tonic-gate 		aptr++;
910*7c478bd9Sstevel@tonic-gate 
911*7c478bd9Sstevel@tonic-gate 	if (aptr != NULL) {
912*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
913*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\t(auth_is) auth type is %s "
914*7c478bd9Sstevel@tonic-gate 				"(%d bytes)\n",	aptr->AuthString, cnt);
915*7c478bd9Sstevel@tonic-gate 
916*7c478bd9Sstevel@tonic-gate 		if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
917*7c478bd9Sstevel@tonic-gate 			kerberos5_is(aptr, data+2, cnt-2);
918*7c478bd9Sstevel@tonic-gate 	}
919*7c478bd9Sstevel@tonic-gate }
920*7c478bd9Sstevel@tonic-gate 
921*7c478bd9Sstevel@tonic-gate static int
922*7c478bd9Sstevel@tonic-gate krb5_user_status(char *name, int namelen, int level)
923*7c478bd9Sstevel@tonic-gate {
924*7c478bd9Sstevel@tonic-gate 	int retval = AUTH_USER;
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 	if (auth_debug)
927*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(krb5_user_status) level = %d "
928*7c478bd9Sstevel@tonic-gate 			"auth_level = %d  user = %s\n",
929*7c478bd9Sstevel@tonic-gate 			level, auth_level,
930*7c478bd9Sstevel@tonic-gate 			(AuthenticatingUser != NULL ? AuthenticatingUser : ""));
931*7c478bd9Sstevel@tonic-gate 
932*7c478bd9Sstevel@tonic-gate 	if (level < AUTH_USER)
933*7c478bd9Sstevel@tonic-gate 		return (level);
934*7c478bd9Sstevel@tonic-gate 
935*7c478bd9Sstevel@tonic-gate 	if (AuthenticatingUser != NULL &&
936*7c478bd9Sstevel@tonic-gate 	    (retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
937*7c478bd9Sstevel@tonic-gate 			    AuthenticatingUser))) {
938*7c478bd9Sstevel@tonic-gate 		(void) strncpy(name, AuthenticatingUser, namelen);
939*7c478bd9Sstevel@tonic-gate 		return (AUTH_VALID);
940*7c478bd9Sstevel@tonic-gate 	} else {
941*7c478bd9Sstevel@tonic-gate 		if (!retval)
942*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR,
943*7c478bd9Sstevel@tonic-gate 			    "Krb5 principal lacks permission to "
944*7c478bd9Sstevel@tonic-gate 			    "access local account for %s",
945*7c478bd9Sstevel@tonic-gate 			    AuthenticatingUser);
946*7c478bd9Sstevel@tonic-gate 		return (AUTH_USER);
947*7c478bd9Sstevel@tonic-gate 	}
948*7c478bd9Sstevel@tonic-gate }
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate /*
951*7c478bd9Sstevel@tonic-gate  * Wrapper around /dev/urandom
952*7c478bd9Sstevel@tonic-gate  */
953*7c478bd9Sstevel@tonic-gate static int
954*7c478bd9Sstevel@tonic-gate getrandom(char *buf, int buflen)
955*7c478bd9Sstevel@tonic-gate {
956*7c478bd9Sstevel@tonic-gate 	static int devrandom = -1;
957*7c478bd9Sstevel@tonic-gate 
958*7c478bd9Sstevel@tonic-gate 	if (devrandom == -1 &&
959*7c478bd9Sstevel@tonic-gate 	    (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
960*7c478bd9Sstevel@tonic-gate 		fatalperror(net, "Unable to open /dev/urandom: ",
961*7c478bd9Sstevel@tonic-gate 			    errno);
962*7c478bd9Sstevel@tonic-gate 		return (-1);
963*7c478bd9Sstevel@tonic-gate 	}
964*7c478bd9Sstevel@tonic-gate 
965*7c478bd9Sstevel@tonic-gate 	if (read(devrandom, buf, buflen) == -1) {
966*7c478bd9Sstevel@tonic-gate 		fatalperror(net, "Unable to read from /dev/urandom: ",
967*7c478bd9Sstevel@tonic-gate 			    errno);
968*7c478bd9Sstevel@tonic-gate 		return (-1);
969*7c478bd9Sstevel@tonic-gate 	}
970*7c478bd9Sstevel@tonic-gate 
971*7c478bd9Sstevel@tonic-gate 	return (0);
972*7c478bd9Sstevel@tonic-gate }
973*7c478bd9Sstevel@tonic-gate 
974*7c478bd9Sstevel@tonic-gate /*
975*7c478bd9Sstevel@tonic-gate  * encrypt_init
976*7c478bd9Sstevel@tonic-gate  *
977*7c478bd9Sstevel@tonic-gate  * Initialize the encryption data structures
978*7c478bd9Sstevel@tonic-gate  */
979*7c478bd9Sstevel@tonic-gate static void
980*7c478bd9Sstevel@tonic-gate encrypt_init()
981*7c478bd9Sstevel@tonic-gate {
982*7c478bd9Sstevel@tonic-gate 	(void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
983*7c478bd9Sstevel@tonic-gate 	(void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
984*7c478bd9Sstevel@tonic-gate 
985*7c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = ENCR_STATE_NOT_READY;
986*7c478bd9Sstevel@tonic-gate 	encr_data.decrypt.state = ENCR_STATE_NOT_READY;
987*7c478bd9Sstevel@tonic-gate }
988*7c478bd9Sstevel@tonic-gate 
989*7c478bd9Sstevel@tonic-gate /*
990*7c478bd9Sstevel@tonic-gate  * encrypt_send_request_start
991*7c478bd9Sstevel@tonic-gate  *
992*7c478bd9Sstevel@tonic-gate  * Request that the remote side automatically start sending
993*7c478bd9Sstevel@tonic-gate  * encrypted output
994*7c478bd9Sstevel@tonic-gate  */
995*7c478bd9Sstevel@tonic-gate static void
996*7c478bd9Sstevel@tonic-gate encrypt_send_request_start()
997*7c478bd9Sstevel@tonic-gate {
998*7c478bd9Sstevel@tonic-gate 	uchar_t buf[6+TELNET_MAXKEYIDLEN], *p;
999*7c478bd9Sstevel@tonic-gate 
1000*7c478bd9Sstevel@tonic-gate 	p = buf;
1001*7c478bd9Sstevel@tonic-gate 
1002*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1003*7c478bd9Sstevel@tonic-gate 	*p++ = SB;
1004*7c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
1005*7c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_REQSTART;
1006*7c478bd9Sstevel@tonic-gate 	/*
1007*7c478bd9Sstevel@tonic-gate 	 * We are telling the remote side which
1008*7c478bd9Sstevel@tonic-gate 	 * decrypt key we will use so that it may
1009*7c478bd9Sstevel@tonic-gate 	 * encrypt in the same key.
1010*7c478bd9Sstevel@tonic-gate 	 */
1011*7c478bd9Sstevel@tonic-gate 	(void) memcpy(p, encr_data.decrypt.keyid, encr_data.decrypt.keyidlen);
1012*7c478bd9Sstevel@tonic-gate 	p += encr_data.decrypt.keyidlen;
1013*7c478bd9Sstevel@tonic-gate 
1014*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1015*7c478bd9Sstevel@tonic-gate 	*p++ = SE;
1016*7c478bd9Sstevel@tonic-gate 
1017*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)buf, p-buf);
1018*7c478bd9Sstevel@tonic-gate 	netflush();
1019*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1020*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1021*7c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_REQSTART\n");
1022*7c478bd9Sstevel@tonic-gate }
1023*7c478bd9Sstevel@tonic-gate 
1024*7c478bd9Sstevel@tonic-gate /*
1025*7c478bd9Sstevel@tonic-gate  * encrypt_is
1026*7c478bd9Sstevel@tonic-gate  *
1027*7c478bd9Sstevel@tonic-gate  * When we receive the TELOPT_ENCRYPT ENCRYPT_IS ...
1028*7c478bd9Sstevel@tonic-gate  * message, the client is telling us that it will be sending
1029*7c478bd9Sstevel@tonic-gate  * encrypted data using the indicated cipher.
1030*7c478bd9Sstevel@tonic-gate  * We must initialize the read (decrypt) side of our connection
1031*7c478bd9Sstevel@tonic-gate  */
1032*7c478bd9Sstevel@tonic-gate static void
1033*7c478bd9Sstevel@tonic-gate encrypt_is(uchar_t *data, int cnt)
1034*7c478bd9Sstevel@tonic-gate {
1035*7c478bd9Sstevel@tonic-gate 	register int type;
1036*7c478bd9Sstevel@tonic-gate 	register int iv_status = CFB64_IV_OK;
1037*7c478bd9Sstevel@tonic-gate 	register int lstate = 0;
1038*7c478bd9Sstevel@tonic-gate 
1039*7c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[] = {
1040*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1041*7c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
1042*7c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
1043*7c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_REPLY,
1044*7c478bd9Sstevel@tonic-gate 		(uchar_t)0,		/* placeholder:  sbbuf[4] */
1045*7c478bd9Sstevel@tonic-gate 		(uchar_t)CFB64_IV_OK,	/* placeholder:  sbbuf[5] */
1046*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1047*7c478bd9Sstevel@tonic-gate 		(uchar_t)SE,
1048*7c478bd9Sstevel@tonic-gate 	};
1049*7c478bd9Sstevel@tonic-gate 
1050*7c478bd9Sstevel@tonic-gate 	if (--cnt < 0)
1051*7c478bd9Sstevel@tonic-gate 		return;
1052*7c478bd9Sstevel@tonic-gate 
1053*7c478bd9Sstevel@tonic-gate 	type = sbbuf[4] = *data++;
1054*7c478bd9Sstevel@tonic-gate 
1055*7c478bd9Sstevel@tonic-gate 	/*
1056*7c478bd9Sstevel@tonic-gate 	 * Steps to take:
1057*7c478bd9Sstevel@tonic-gate 	 *   1. Create the proper stream Initialization vector
1058*7c478bd9Sstevel@tonic-gate 	 *		- copy the correct 'seed' to IV and output blocks
1059*7c478bd9Sstevel@tonic-gate 	 *		- set the correct key schedule
1060*7c478bd9Sstevel@tonic-gate 	 *   2. Generate reply for the other side:
1061*7c478bd9Sstevel@tonic-gate 	 *		IAC SB TELOPT_ENCRYPT ENCRYPT_REPLY type CFB64_IV_OK
1062*7c478bd9Sstevel@tonic-gate 	 *		[ data ... ] IAC SE
1063*7c478bd9Sstevel@tonic-gate 	 *   3. Tell crypto module:  method, direction, IV
1064*7c478bd9Sstevel@tonic-gate 	 */
1065*7c478bd9Sstevel@tonic-gate 	switch (type) {
1066*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCTYPE_DES_CFB64:
1067*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = type;
1068*7c478bd9Sstevel@tonic-gate 
1069*7c478bd9Sstevel@tonic-gate 		lstate = encr_data.decrypt.state;
1070*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1071*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1072*7c478bd9Sstevel@tonic-gate 				    "\t(encrypt_is) initial state = %d\n",
1073*7c478bd9Sstevel@tonic-gate 				    lstate);
1074*7c478bd9Sstevel@tonic-gate 		/*
1075*7c478bd9Sstevel@tonic-gate 		 * Before we extract the IV bytes, make sure we got
1076*7c478bd9Sstevel@tonic-gate 		 * enough data.
1077*7c478bd9Sstevel@tonic-gate 		 */
1078*7c478bd9Sstevel@tonic-gate 		if (cnt < sizeof (Block)) {
1079*7c478bd9Sstevel@tonic-gate 			iv_status = CFB64_IV_BAD;
1080*7c478bd9Sstevel@tonic-gate 			if (enc_debug)
1081*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1082*7c478bd9Sstevel@tonic-gate 					    "\t(encrypt_is) Not enough "
1083*7c478bd9Sstevel@tonic-gate 					    "IV bytes\n");
1084*7c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_NOT_READY;
1085*7c478bd9Sstevel@tonic-gate 		} else {
1086*7c478bd9Sstevel@tonic-gate 			data++; /* skip over the CFB64_IV byte */
1087*7c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.decrypt.ivec, data,
1088*7c478bd9Sstevel@tonic-gate 				    sizeof (Block));
1089*7c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
1090*7c478bd9Sstevel@tonic-gate 		}
1091*7c478bd9Sstevel@tonic-gate 		break;
1092*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCTYPE_NULL:
1093*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = type;
1094*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV;
1095*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV;
1096*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1097*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1098*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_is) We accept NULL encr\n");
1099*7c478bd9Sstevel@tonic-gate 		break;
1100*7c478bd9Sstevel@tonic-gate 	default:
1101*7c478bd9Sstevel@tonic-gate 		iv_status = CFB64_IV_BAD;
1102*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.type = NULL;
1103*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1104*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1105*7c478bd9Sstevel@tonic-gate 				    "\t(encrypt_is) Can't find type (%d) "
1106*7c478bd9Sstevel@tonic-gate 				    "for initial negotiation\r\n",
1107*7c478bd9Sstevel@tonic-gate 				    type);
1108*7c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
1109*7c478bd9Sstevel@tonic-gate 		break;
1110*7c478bd9Sstevel@tonic-gate 	}
1111*7c478bd9Sstevel@tonic-gate 
1112*7c478bd9Sstevel@tonic-gate 	sbbuf[5] = (uchar_t)iv_status; /* either CFB64_IV_OK or BAD */
1113*7c478bd9Sstevel@tonic-gate 
1114*7c478bd9Sstevel@tonic-gate 	if (iv_status == CFB64_IV_OK) {
1115*7c478bd9Sstevel@tonic-gate 		/*
1116*7c478bd9Sstevel@tonic-gate 		 * send IV to crypto module and indicate it is for
1117*7c478bd9Sstevel@tonic-gate 		 * decrypt only
1118*7c478bd9Sstevel@tonic-gate 		 */
1119*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV;  /* we received an OK IV */
1120*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV;  /* we dont send an IV */
1121*7c478bd9Sstevel@tonic-gate 	} else {
1122*7c478bd9Sstevel@tonic-gate 		/* tell crypto module to disable crypto on "read" stream */
1123*7c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
1124*7c478bd9Sstevel@tonic-gate 	}
1125*7c478bd9Sstevel@tonic-gate 
1126*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, sizeof (sbbuf));
1127*7c478bd9Sstevel@tonic-gate 	netflush();
1128*7c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
1129*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1130*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1131*7c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_REPLY %s %s\n",
1132*7c478bd9Sstevel@tonic-gate 			    ENCTYPE_NAME(type),
1133*7c478bd9Sstevel@tonic-gate 			    (iv_status == CFB64_IV_OK ? "CFB64_IV_OK" :
1134*7c478bd9Sstevel@tonic-gate 			    "CFB64_IV_BAD"));
1135*7c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
1136*7c478bd9Sstevel@tonic-gate 	/* Update the state of the decryption negotiation */
1137*7c478bd9Sstevel@tonic-gate 	encr_data.decrypt.state = lstate;
1138*7c478bd9Sstevel@tonic-gate 
1139*7c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY)
1140*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.autoflag = 0;
1141*7c478bd9Sstevel@tonic-gate 	else {
1142*7c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_OK && encr_data.decrypt.autoflag)
1143*7c478bd9Sstevel@tonic-gate 			encrypt_send_request_start();
1144*7c478bd9Sstevel@tonic-gate 	}
1145*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1146*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1147*7c478bd9Sstevel@tonic-gate 			    "\t(encrypt_is) final DECRYPT state = %d\n",
1148*7c478bd9Sstevel@tonic-gate 			    encr_data.decrypt.state);
1149*7c478bd9Sstevel@tonic-gate }
1150*7c478bd9Sstevel@tonic-gate 
1151*7c478bd9Sstevel@tonic-gate /*
1152*7c478bd9Sstevel@tonic-gate  * encrypt_send_encrypt_is
1153*7c478bd9Sstevel@tonic-gate  *
1154*7c478bd9Sstevel@tonic-gate  * Tell the client what encryption we will use
1155*7c478bd9Sstevel@tonic-gate  * and what our IV will be.
1156*7c478bd9Sstevel@tonic-gate  */
1157*7c478bd9Sstevel@tonic-gate static int
1158*7c478bd9Sstevel@tonic-gate encrypt_send_encrypt_is()
1159*7c478bd9Sstevel@tonic-gate {
1160*7c478bd9Sstevel@tonic-gate 	register int lstate;
1161*7c478bd9Sstevel@tonic-gate 	krb5_error_code kret;
1162*7c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[MAXOPTLEN], *p;
1163*7c478bd9Sstevel@tonic-gate 	int i;
1164*7c478bd9Sstevel@tonic-gate 
1165*7c478bd9Sstevel@tonic-gate 	lstate = encr_data.encrypt.state;
1166*7c478bd9Sstevel@tonic-gate 
1167*7c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == ENCTYPE_NULL) {
1168*7c478bd9Sstevel@tonic-gate 		/*
1169*7c478bd9Sstevel@tonic-gate 		 * Haven't received ENCRYPT SUPPORT yet or we couldn't agree
1170*7c478bd9Sstevel@tonic-gate 		 * on a cipher.
1171*7c478bd9Sstevel@tonic-gate 		 */
1172*7c478bd9Sstevel@tonic-gate 		return (lstate);
1173*7c478bd9Sstevel@tonic-gate 	}
1174*7c478bd9Sstevel@tonic-gate 
1175*7c478bd9Sstevel@tonic-gate 	/*
1176*7c478bd9Sstevel@tonic-gate 	 * - Create a random DES key
1177*7c478bd9Sstevel@tonic-gate 	 *
1178*7c478bd9Sstevel@tonic-gate 	 * - DES ECB encrypt
1179*7c478bd9Sstevel@tonic-gate 	 *   encrypt the IV using itself as the key.
1180*7c478bd9Sstevel@tonic-gate 	 *
1181*7c478bd9Sstevel@tonic-gate 	 * - Send response
1182*7c478bd9Sstevel@tonic-gate 	 *   IAC SB TELOPT_ENCRYPT ENCRYPT_IS CFB64 FB64_IV [ feed block ]
1183*7c478bd9Sstevel@tonic-gate 	 *   IAC SE
1184*7c478bd9Sstevel@tonic-gate 	 *
1185*7c478bd9Sstevel@tonic-gate 	 */
1186*7c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY)
1187*7c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_IN_PROGRESS;
1188*7c478bd9Sstevel@tonic-gate 	else if ((lstate & ENCR_STATE_NO_SEND_IV) == 0) {
1189*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1190*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1191*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_send_is) IV already sent,"
1192*7c478bd9Sstevel@tonic-gate 				" state = %d\n", lstate);
1193*7c478bd9Sstevel@tonic-gate 		return (lstate);
1194*7c478bd9Sstevel@tonic-gate 	}
1195*7c478bd9Sstevel@tonic-gate 
1196*7c478bd9Sstevel@tonic-gate 	if (!VALIDKEY(encr_data.encrypt.krbdes_key)) {
1197*7c478bd9Sstevel@tonic-gate 		/*
1198*7c478bd9Sstevel@tonic-gate 		 * Invalid key, set flag so we try again later
1199*7c478bd9Sstevel@tonic-gate 		 * when we get a good one
1200*7c478bd9Sstevel@tonic-gate 		 */
1201*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.need_start = 1;
1202*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1203*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1204*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_send_is) No Key, cannot "
1205*7c478bd9Sstevel@tonic-gate 				"start encryption yet\n");
1206*7c478bd9Sstevel@tonic-gate 		return (lstate);
1207*7c478bd9Sstevel@tonic-gate 	}
1208*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1209*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1210*7c478bd9Sstevel@tonic-gate 			    "\t(encrypt_send_is) Creating new feed\n");
1211*7c478bd9Sstevel@tonic-gate 
1212*7c478bd9Sstevel@tonic-gate 	/*
1213*7c478bd9Sstevel@tonic-gate 	 * Create a random feed and send it over.
1214*7c478bd9Sstevel@tonic-gate 	 *
1215*7c478bd9Sstevel@tonic-gate 	 * Use the /dev/[u]random interface to generate
1216*7c478bd9Sstevel@tonic-gate 	 * our encryption IV.
1217*7c478bd9Sstevel@tonic-gate 	 */
1218*7c478bd9Sstevel@tonic-gate 	kret = getrandom((char *)encr_data.encrypt.ivec, sizeof (Block));
1219*7c478bd9Sstevel@tonic-gate 
1220*7c478bd9Sstevel@tonic-gate 	if (kret) {
1221*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1222*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1223*7c478bd9Sstevel@tonic-gate 				    "\t(encrypt_send_is) error from "
1224*7c478bd9Sstevel@tonic-gate 				    "getrandom: %d\n", kret);
1225*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Failed to create encryption key (err %d)\n");
1226*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = ENCTYPE_NULL;
1227*7c478bd9Sstevel@tonic-gate 	} else {
1228*7c478bd9Sstevel@tonic-gate 		mit_des_fixup_key_parity(encr_data.encrypt.ivec);
1229*7c478bd9Sstevel@tonic-gate 	}
1230*7c478bd9Sstevel@tonic-gate 
1231*7c478bd9Sstevel@tonic-gate 	p = sbbuf;
1232*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1233*7c478bd9Sstevel@tonic-gate 	*p++ = SB;
1234*7c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
1235*7c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_IS;
1236*7c478bd9Sstevel@tonic-gate 	*p++ = encr_data.encrypt.type;
1237*7c478bd9Sstevel@tonic-gate 	*p++ = CFB64_IV;
1238*7c478bd9Sstevel@tonic-gate 
1239*7c478bd9Sstevel@tonic-gate 	/*
1240*7c478bd9Sstevel@tonic-gate 	 * Copy the IV bytes individually so that when a
1241*7c478bd9Sstevel@tonic-gate 	 * 255 (telnet IAC) is used, it can be "escaped" by
1242*7c478bd9Sstevel@tonic-gate 	 * adding it twice (telnet RFC 854).
1243*7c478bd9Sstevel@tonic-gate 	 */
1244*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < sizeof (Block); i++)
1245*7c478bd9Sstevel@tonic-gate 		if ((*p++ = encr_data.encrypt.ivec[i]) == IAC)
1246*7c478bd9Sstevel@tonic-gate 			*p++ = IAC;
1247*7c478bd9Sstevel@tonic-gate 
1248*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1249*7c478bd9Sstevel@tonic-gate 	*p++ = SE;
1250*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
1251*7c478bd9Sstevel@tonic-gate 	netflush();
1252*7c478bd9Sstevel@tonic-gate 
1253*7c478bd9Sstevel@tonic-gate 	if (!kret) {
1254*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* we sent our IV */
1255*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* dont need decrypt IV */
1256*7c478bd9Sstevel@tonic-gate 	}
1257*7c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
1258*7c478bd9Sstevel@tonic-gate 
1259*7c478bd9Sstevel@tonic-gate 	if (enc_debug) {
1260*7c478bd9Sstevel@tonic-gate 		int i;
1261*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1262*7c478bd9Sstevel@tonic-gate 			    "SENT TELOPT_ENCRYPT ENCRYPT_IS %d CFB64_IV ",
1263*7c478bd9Sstevel@tonic-gate 			    encr_data.encrypt.type);
1264*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < (p-sbbuf); i++)
1265*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%d ", (int)sbbuf[i]);
1266*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
1267*7c478bd9Sstevel@tonic-gate 	}
1268*7c478bd9Sstevel@tonic-gate 
1269*7c478bd9Sstevel@tonic-gate 	return (lstate);
1270*7c478bd9Sstevel@tonic-gate }
1271*7c478bd9Sstevel@tonic-gate 
1272*7c478bd9Sstevel@tonic-gate /*
1273*7c478bd9Sstevel@tonic-gate  * stop_stream
1274*7c478bd9Sstevel@tonic-gate  *
1275*7c478bd9Sstevel@tonic-gate  * Utility routine to send a CRIOCSTOP ioctl to the
1276*7c478bd9Sstevel@tonic-gate  * crypto module (cryptmod).
1277*7c478bd9Sstevel@tonic-gate  */
1278*7c478bd9Sstevel@tonic-gate static void
1279*7c478bd9Sstevel@tonic-gate stop_stream(int fd, int dir)
1280*7c478bd9Sstevel@tonic-gate {
1281*7c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
1282*7c478bd9Sstevel@tonic-gate 	uint32_t stopdir = dir;
1283*7c478bd9Sstevel@tonic-gate 
1284*7c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSTOP;
1285*7c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
1286*7c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (stopdir);
1287*7c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&stopdir;
1288*7c478bd9Sstevel@tonic-gate 
1289*7c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_STR, &crioc)) {
1290*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
1291*7c478bd9Sstevel@tonic-gate 	}
1292*7c478bd9Sstevel@tonic-gate }
1293*7c478bd9Sstevel@tonic-gate 
1294*7c478bd9Sstevel@tonic-gate /*
1295*7c478bd9Sstevel@tonic-gate  * start_stream
1296*7c478bd9Sstevel@tonic-gate  *
1297*7c478bd9Sstevel@tonic-gate  * Utility routine to send a CRYPTIOCSTART ioctl to the
1298*7c478bd9Sstevel@tonic-gate  * crypto module (cryptmod).  This routine may contain optional
1299*7c478bd9Sstevel@tonic-gate  * payload data that the cryptmod will interpret as bytes that
1300*7c478bd9Sstevel@tonic-gate  * need to be decrypted and sent back up to the application
1301*7c478bd9Sstevel@tonic-gate  * via the data stream.
1302*7c478bd9Sstevel@tonic-gate  */
1303*7c478bd9Sstevel@tonic-gate static void
1304*7c478bd9Sstevel@tonic-gate start_stream(int fd, int dir, int datalen, char *data)
1305*7c478bd9Sstevel@tonic-gate {
1306*7c478bd9Sstevel@tonic-gate 	struct strioctl crioc;
1307*7c478bd9Sstevel@tonic-gate 
1308*7c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = (dir == CRYPT_ENCRYPT ? CRYPTIOCSTARTENC :
1309*7c478bd9Sstevel@tonic-gate 			CRYPTIOCSTARTDEC);
1310*7c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
1311*7c478bd9Sstevel@tonic-gate 	crioc.ic_len = datalen;
1312*7c478bd9Sstevel@tonic-gate 	crioc.ic_dp = data;
1313*7c478bd9Sstevel@tonic-gate 
1314*7c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_STR, &crioc)) {
1315*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
1316*7c478bd9Sstevel@tonic-gate 	}
1317*7c478bd9Sstevel@tonic-gate }
1318*7c478bd9Sstevel@tonic-gate 
1319*7c478bd9Sstevel@tonic-gate /*
1320*7c478bd9Sstevel@tonic-gate  * encrypt_start_output
1321*7c478bd9Sstevel@tonic-gate  *
1322*7c478bd9Sstevel@tonic-gate  * Tell the other side to start encrypting its data
1323*7c478bd9Sstevel@tonic-gate  */
1324*7c478bd9Sstevel@tonic-gate static void
1325*7c478bd9Sstevel@tonic-gate encrypt_start_output()
1326*7c478bd9Sstevel@tonic-gate {
1327*7c478bd9Sstevel@tonic-gate 	int lstate;
1328*7c478bd9Sstevel@tonic-gate 	uchar_t *p;
1329*7c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[MAXOPTLEN];
1330*7c478bd9Sstevel@tonic-gate 	struct strioctl crioc;
1331*7c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
1332*7c478bd9Sstevel@tonic-gate 
1333*7c478bd9Sstevel@tonic-gate 	/*
1334*7c478bd9Sstevel@tonic-gate 	 * Initialize crypto and send the ENCRYPT_IS msg
1335*7c478bd9Sstevel@tonic-gate 	 */
1336*7c478bd9Sstevel@tonic-gate 	lstate = encrypt_send_encrypt_is();
1337*7c478bd9Sstevel@tonic-gate 
1338*7c478bd9Sstevel@tonic-gate 	if (lstate != ENCR_STATE_OK) {
1339*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1340*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1341*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_start_output) ENCRYPT state "
1342*7c478bd9Sstevel@tonic-gate 				"= %d\n", lstate);
1343*7c478bd9Sstevel@tonic-gate 		return;
1344*7c478bd9Sstevel@tonic-gate 	}
1345*7c478bd9Sstevel@tonic-gate 
1346*7c478bd9Sstevel@tonic-gate 	p = sbbuf;
1347*7c478bd9Sstevel@tonic-gate 
1348*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1349*7c478bd9Sstevel@tonic-gate 	*p++ = SB;
1350*7c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
1351*7c478bd9Sstevel@tonic-gate 	*p++ = ENCRYPT_START;
1352*7c478bd9Sstevel@tonic-gate 
1353*7c478bd9Sstevel@tonic-gate 	(void) memcpy(p, encr_data.encrypt.keyid, encr_data.encrypt.keyidlen);
1354*7c478bd9Sstevel@tonic-gate 	p += encr_data.encrypt.keyidlen;
1355*7c478bd9Sstevel@tonic-gate 
1356*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1357*7c478bd9Sstevel@tonic-gate 	*p++ = SE;
1358*7c478bd9Sstevel@tonic-gate 
1359*7c478bd9Sstevel@tonic-gate 	/* Flush this data out before we start encrypting */
1360*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (int)(p-sbbuf));
1361*7c478bd9Sstevel@tonic-gate 	netflush();
1362*7c478bd9Sstevel@tonic-gate 
1363*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1364*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_START %d "
1365*7c478bd9Sstevel@tonic-gate 			"(lstate = %d) data waiting = %d\n",
1366*7c478bd9Sstevel@tonic-gate 			(int)encr_data.encrypt.keyid[0],
1367*7c478bd9Sstevel@tonic-gate 			lstate, nfrontp-nbackp);
1368*7c478bd9Sstevel@tonic-gate 
1369*7c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
1370*7c478bd9Sstevel@tonic-gate 
1371*7c478bd9Sstevel@tonic-gate 	/*
1372*7c478bd9Sstevel@tonic-gate 	 * tell crypto module what key to use for encrypting
1373*7c478bd9Sstevel@tonic-gate 	 * Note that the ENCRYPT has not yet been enabled, but we
1374*7c478bd9Sstevel@tonic-gate 	 * need to first set the crypto key to use.
1375*7c478bd9Sstevel@tonic-gate 	 */
1376*7c478bd9Sstevel@tonic-gate 	cki.direction_mask = CRYPT_ENCRYPT;
1377*7c478bd9Sstevel@tonic-gate 
1378*7c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
1379*7c478bd9Sstevel@tonic-gate 		cki.crypto_method = CRYPT_METHOD_DES_CFB;
1380*7c478bd9Sstevel@tonic-gate 	} else {
1381*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1382*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1383*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_start_output) - unknown "
1384*7c478bd9Sstevel@tonic-gate 				"crypto_method %d\n",
1385*7c478bd9Sstevel@tonic-gate 				encr_data.encrypt.type);
1386*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unrecognized crypto encrypt method: %d",
1387*7c478bd9Sstevel@tonic-gate 				encr_data.encrypt.type);
1388*7c478bd9Sstevel@tonic-gate 
1389*7c478bd9Sstevel@tonic-gate 		return;
1390*7c478bd9Sstevel@tonic-gate 	}
1391*7c478bd9Sstevel@tonic-gate 
1392*7c478bd9Sstevel@tonic-gate 	/*
1393*7c478bd9Sstevel@tonic-gate 	 * If we previously configured this crypto method, we dont want to
1394*7c478bd9Sstevel@tonic-gate 	 * overwrite the key or ivec information already given to the crypto
1395*7c478bd9Sstevel@tonic-gate 	 * module as it will cause the cipher data between the client and server
1396*7c478bd9Sstevel@tonic-gate 	 * to become out of synch and impossible to decipher.
1397*7c478bd9Sstevel@tonic-gate 	 */
1398*7c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.setup == cki.crypto_method) {
1399*7c478bd9Sstevel@tonic-gate 		cki.keylen = 0;
1400*7c478bd9Sstevel@tonic-gate 		cki.iveclen = 0;
1401*7c478bd9Sstevel@tonic-gate 	} else {
1402*7c478bd9Sstevel@tonic-gate 		cki.keylen = DES_BLOCKSIZE;
1403*7c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.key, (void *)encr_data.encrypt.krbdes_key,
1404*7c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
1405*7c478bd9Sstevel@tonic-gate 
1406*7c478bd9Sstevel@tonic-gate 		cki.iveclen = DES_BLOCKSIZE;
1407*7c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.ivec, (void *)encr_data.encrypt.ivec,
1408*7c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
1409*7c478bd9Sstevel@tonic-gate 
1410*7c478bd9Sstevel@tonic-gate 		cki.ivec_usage = IVEC_ONETIME;
1411*7c478bd9Sstevel@tonic-gate 	}
1412*7c478bd9Sstevel@tonic-gate 
1413*7c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
1414*7c478bd9Sstevel@tonic-gate 
1415*7c478bd9Sstevel@tonic-gate 	/* Stop encrypt side prior to setup so we dont lose data */
1416*7c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, CRYPT_ENCRYPT);
1417*7c478bd9Sstevel@tonic-gate 
1418*7c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
1419*7c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
1420*7c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (struct cr_info_t);
1421*7c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
1422*7c478bd9Sstevel@tonic-gate 
1423*7c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
1424*7c478bd9Sstevel@tonic-gate 		perror("ioctl(CRYPTIOCSETUP) [encrypt_start_output] error");
1425*7c478bd9Sstevel@tonic-gate 	} else {
1426*7c478bd9Sstevel@tonic-gate 		/* Setup completed OK */
1427*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.setup = cki.crypto_method;
1428*7c478bd9Sstevel@tonic-gate 	}
1429*7c478bd9Sstevel@tonic-gate 
1430*7c478bd9Sstevel@tonic-gate 	/*
1431*7c478bd9Sstevel@tonic-gate 	 * We do not check for "stuck" data when setting up the
1432*7c478bd9Sstevel@tonic-gate 	 * outbound "encrypt" channel.  Any data queued prior to
1433*7c478bd9Sstevel@tonic-gate 	 * this IOCTL will get processed correctly without our help.
1434*7c478bd9Sstevel@tonic-gate 	 */
1435*7c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, CRYPT_ENCRYPT, 0, NULL);
1436*7c478bd9Sstevel@tonic-gate 
1437*7c478bd9Sstevel@tonic-gate 	/*
1438*7c478bd9Sstevel@tonic-gate 	 * tell crypto module to start encrypting
1439*7c478bd9Sstevel@tonic-gate 	 */
1440*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1441*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1442*7c478bd9Sstevel@tonic-gate 			"\t(encrypt_start_output) Encrypting output\n");
1443*7c478bd9Sstevel@tonic-gate }
1444*7c478bd9Sstevel@tonic-gate 
1445*7c478bd9Sstevel@tonic-gate /*
1446*7c478bd9Sstevel@tonic-gate  * encrypt_request_start
1447*7c478bd9Sstevel@tonic-gate  *
1448*7c478bd9Sstevel@tonic-gate  * The client requests that we start encryption immediately after
1449*7c478bd9Sstevel@tonic-gate  * successful negotiation
1450*7c478bd9Sstevel@tonic-gate  */
1451*7c478bd9Sstevel@tonic-gate static void
1452*7c478bd9Sstevel@tonic-gate encrypt_request_start(void)
1453*7c478bd9Sstevel@tonic-gate {
1454*7c478bd9Sstevel@tonic-gate 	if (encr_data.encrypt.type == ENCTYPE_NULL) {
1455*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 1;
1456*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1457*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\t(encrypt_request_start) "
1458*7c478bd9Sstevel@tonic-gate 				"autoencrypt = ON\n");
1459*7c478bd9Sstevel@tonic-gate 	} else {
1460*7c478bd9Sstevel@tonic-gate 		encrypt_start_output();
1461*7c478bd9Sstevel@tonic-gate 	}
1462*7c478bd9Sstevel@tonic-gate }
1463*7c478bd9Sstevel@tonic-gate 
1464*7c478bd9Sstevel@tonic-gate /*
1465*7c478bd9Sstevel@tonic-gate  * encrypt_end
1466*7c478bd9Sstevel@tonic-gate  *
1467*7c478bd9Sstevel@tonic-gate  * ENCRYPT END received, stop decrypting the read stream
1468*7c478bd9Sstevel@tonic-gate  */
1469*7c478bd9Sstevel@tonic-gate static void
1470*7c478bd9Sstevel@tonic-gate encrypt_end(int direction)
1471*7c478bd9Sstevel@tonic-gate {
1472*7c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
1473*7c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
1474*7c478bd9Sstevel@tonic-gate 	uint32_t stopdir;
1475*7c478bd9Sstevel@tonic-gate 
1476*7c478bd9Sstevel@tonic-gate 	stopdir = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
1477*7c478bd9Sstevel@tonic-gate 		CRYPT_ENCRYPT);
1478*7c478bd9Sstevel@tonic-gate 
1479*7c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, stopdir);
1480*7c478bd9Sstevel@tonic-gate 
1481*7c478bd9Sstevel@tonic-gate 	/*
1482*7c478bd9Sstevel@tonic-gate 	 * Call this function when we wish to disable crypto in
1483*7c478bd9Sstevel@tonic-gate 	 * either direction (ENCRYPT or DECRYPT)
1484*7c478bd9Sstevel@tonic-gate 	 */
1485*7c478bd9Sstevel@tonic-gate 	cki.direction_mask = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
1486*7c478bd9Sstevel@tonic-gate 			    CRYPT_ENCRYPT);
1487*7c478bd9Sstevel@tonic-gate 	cki.crypto_method = CRYPT_METHOD_NONE;
1488*7c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
1489*7c478bd9Sstevel@tonic-gate 
1490*7c478bd9Sstevel@tonic-gate 	cki.keylen = 0;
1491*7c478bd9Sstevel@tonic-gate 	cki.iveclen = 0;
1492*7c478bd9Sstevel@tonic-gate 
1493*7c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
1494*7c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
1495*7c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (cki);
1496*7c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
1497*7c478bd9Sstevel@tonic-gate 
1498*7c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
1499*7c478bd9Sstevel@tonic-gate 		perror("ioctl(CRYPTIOCSETUP) [encrypt_end] error");
1500*7c478bd9Sstevel@tonic-gate 	}
1501*7c478bd9Sstevel@tonic-gate 
1502*7c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, stopdir, 0, NULL);
1503*7c478bd9Sstevel@tonic-gate }
1504*7c478bd9Sstevel@tonic-gate 
1505*7c478bd9Sstevel@tonic-gate /*
1506*7c478bd9Sstevel@tonic-gate  * encrypt_request_end
1507*7c478bd9Sstevel@tonic-gate  *
1508*7c478bd9Sstevel@tonic-gate  * When we receive a REQEND from the client, it means
1509*7c478bd9Sstevel@tonic-gate  * that we are supposed to stop encrypting
1510*7c478bd9Sstevel@tonic-gate  */
1511*7c478bd9Sstevel@tonic-gate static void
1512*7c478bd9Sstevel@tonic-gate encrypt_request_end()
1513*7c478bd9Sstevel@tonic-gate {
1514*7c478bd9Sstevel@tonic-gate 	/*
1515*7c478bd9Sstevel@tonic-gate 	 * Tell the other side we are done encrypting
1516*7c478bd9Sstevel@tonic-gate 	 */
1517*7c478bd9Sstevel@tonic-gate 
1518*7c478bd9Sstevel@tonic-gate 	write_data("%c%c%c%c%c%c",
1519*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1520*7c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
1521*7c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
1522*7c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_END,
1523*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1524*7c478bd9Sstevel@tonic-gate 		(uchar_t)SE);
1525*7c478bd9Sstevel@tonic-gate 	netflush();
1526*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1527*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_END\n");
1528*7c478bd9Sstevel@tonic-gate 
1529*7c478bd9Sstevel@tonic-gate 	/*
1530*7c478bd9Sstevel@tonic-gate 	 * Turn off encryption of the write stream
1531*7c478bd9Sstevel@tonic-gate 	 */
1532*7c478bd9Sstevel@tonic-gate 	encrypt_end(TELNET_DIR_ENCRYPT);
1533*7c478bd9Sstevel@tonic-gate }
1534*7c478bd9Sstevel@tonic-gate 
1535*7c478bd9Sstevel@tonic-gate /*
1536*7c478bd9Sstevel@tonic-gate  * encrypt_send_request_end
1537*7c478bd9Sstevel@tonic-gate  *
1538*7c478bd9Sstevel@tonic-gate  * We stop encrypting the write stream and tell the other side about it.
1539*7c478bd9Sstevel@tonic-gate  */
1540*7c478bd9Sstevel@tonic-gate static void
1541*7c478bd9Sstevel@tonic-gate encrypt_send_request_end()
1542*7c478bd9Sstevel@tonic-gate {
1543*7c478bd9Sstevel@tonic-gate 	write_data("%c%c%c%c%c%c",
1544*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1545*7c478bd9Sstevel@tonic-gate 		(uchar_t)SB,
1546*7c478bd9Sstevel@tonic-gate 		(uchar_t)TELOPT_ENCRYPT,
1547*7c478bd9Sstevel@tonic-gate 		(uchar_t)ENCRYPT_REQEND,
1548*7c478bd9Sstevel@tonic-gate 		(uchar_t)IAC,
1549*7c478bd9Sstevel@tonic-gate 		(uchar_t)SE);
1550*7c478bd9Sstevel@tonic-gate 	netflush();
1551*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1552*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_REQEND\n");
1553*7c478bd9Sstevel@tonic-gate }
1554*7c478bd9Sstevel@tonic-gate 
1555*7c478bd9Sstevel@tonic-gate /*
1556*7c478bd9Sstevel@tonic-gate  * encrypt_start
1557*7c478bd9Sstevel@tonic-gate  *
1558*7c478bd9Sstevel@tonic-gate  * The client is going to start sending encrypted data
1559*7c478bd9Sstevel@tonic-gate  * using the previously negotiated cipher (see what we set
1560*7c478bd9Sstevel@tonic-gate  * when we did the REPLY in encrypt_is).
1561*7c478bd9Sstevel@tonic-gate  */
1562*7c478bd9Sstevel@tonic-gate static void
1563*7c478bd9Sstevel@tonic-gate encrypt_start(void)
1564*7c478bd9Sstevel@tonic-gate {
1565*7c478bd9Sstevel@tonic-gate 	struct cr_info_t cki;
1566*7c478bd9Sstevel@tonic-gate 	struct strioctl  crioc;
1567*7c478bd9Sstevel@tonic-gate 	int bytes = 0;
1568*7c478bd9Sstevel@tonic-gate 	char *dataptr = NULL;
1569*7c478bd9Sstevel@tonic-gate 
1570*7c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.type == ENCTYPE_NULL) {
1571*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1572*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1573*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) No DECRYPT method "
1574*7c478bd9Sstevel@tonic-gate 				"defined yet\n");
1575*7c478bd9Sstevel@tonic-gate 		encrypt_send_request_end();
1576*7c478bd9Sstevel@tonic-gate 		return;
1577*7c478bd9Sstevel@tonic-gate 	}
1578*7c478bd9Sstevel@tonic-gate 
1579*7c478bd9Sstevel@tonic-gate 	cki.direction_mask = CRYPT_DECRYPT;
1580*7c478bd9Sstevel@tonic-gate 
1581*7c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
1582*7c478bd9Sstevel@tonic-gate 		cki.crypto_method = CRYPT_METHOD_DES_CFB;
1583*7c478bd9Sstevel@tonic-gate 	} else {
1584*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1585*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1586*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) - unknown "
1587*7c478bd9Sstevel@tonic-gate 				"crypto_method %d\n", encr_data.decrypt.type);
1588*7c478bd9Sstevel@tonic-gate 
1589*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unrecognized crypto decrypt method: %d",
1590*7c478bd9Sstevel@tonic-gate 				encr_data.decrypt.type);
1591*7c478bd9Sstevel@tonic-gate 
1592*7c478bd9Sstevel@tonic-gate 		return;
1593*7c478bd9Sstevel@tonic-gate 	}
1594*7c478bd9Sstevel@tonic-gate 
1595*7c478bd9Sstevel@tonic-gate 	/*
1596*7c478bd9Sstevel@tonic-gate 	 * Don't overwrite previously configured key and ivec info
1597*7c478bd9Sstevel@tonic-gate 	 */
1598*7c478bd9Sstevel@tonic-gate 	if (encr_data.decrypt.setup != cki.crypto_method) {
1599*7c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.key, (void *)encr_data.decrypt.krbdes_key,
1600*7c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
1601*7c478bd9Sstevel@tonic-gate 		(void) memcpy(cki.ivec, (void *)encr_data.decrypt.ivec,
1602*7c478bd9Sstevel@tonic-gate 		    DES_BLOCKSIZE);
1603*7c478bd9Sstevel@tonic-gate 
1604*7c478bd9Sstevel@tonic-gate 		cki.keylen = DES_BLOCKSIZE;
1605*7c478bd9Sstevel@tonic-gate 		cki.iveclen = DES_BLOCKSIZE;
1606*7c478bd9Sstevel@tonic-gate 		cki.ivec_usage = IVEC_ONETIME;
1607*7c478bd9Sstevel@tonic-gate 	} else {
1608*7c478bd9Sstevel@tonic-gate 		cki.keylen = 0;
1609*7c478bd9Sstevel@tonic-gate 		cki.iveclen = 0;
1610*7c478bd9Sstevel@tonic-gate 	}
1611*7c478bd9Sstevel@tonic-gate 	cki.option_mask = 0;
1612*7c478bd9Sstevel@tonic-gate 
1613*7c478bd9Sstevel@tonic-gate 	stop_stream(cryptmod_fd, CRYPT_DECRYPT);
1614*7c478bd9Sstevel@tonic-gate 
1615*7c478bd9Sstevel@tonic-gate 	crioc.ic_cmd = CRYPTIOCSETUP;
1616*7c478bd9Sstevel@tonic-gate 	crioc.ic_timout = -1;
1617*7c478bd9Sstevel@tonic-gate 	crioc.ic_len = sizeof (struct cr_info_t);
1618*7c478bd9Sstevel@tonic-gate 	crioc.ic_dp = (char *)&cki;
1619*7c478bd9Sstevel@tonic-gate 
1620*7c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_STR, &crioc)) {
1621*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "ioctl(CRYPTIOCSETUP) [encrypt_start] "
1622*7c478bd9Sstevel@tonic-gate 		    "error: %m");
1623*7c478bd9Sstevel@tonic-gate 	} else {
1624*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.setup = cki.crypto_method;
1625*7c478bd9Sstevel@tonic-gate 	}
1626*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1627*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1628*7c478bd9Sstevel@tonic-gate 			    "\t(encrypt_start) called CRYPTIOCSETUP for "
1629*7c478bd9Sstevel@tonic-gate 			    "decrypt side\n");
1630*7c478bd9Sstevel@tonic-gate 
1631*7c478bd9Sstevel@tonic-gate 	/*
1632*7c478bd9Sstevel@tonic-gate 	 * Read any data stuck between the cryptmod and the application
1633*7c478bd9Sstevel@tonic-gate 	 * so we can pass it back down to be properly decrypted after
1634*7c478bd9Sstevel@tonic-gate 	 * this operation finishes.
1635*7c478bd9Sstevel@tonic-gate 	 */
1636*7c478bd9Sstevel@tonic-gate 	if (ioctl(cryptmod_fd, I_NREAD, &bytes) < 0) {
1637*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "I_NREAD returned error %m");
1638*7c478bd9Sstevel@tonic-gate 		bytes = 0;
1639*7c478bd9Sstevel@tonic-gate 	}
1640*7c478bd9Sstevel@tonic-gate 
1641*7c478bd9Sstevel@tonic-gate 	/*
1642*7c478bd9Sstevel@tonic-gate 	 * Any data which was read AFTER the ENCRYPT START message
1643*7c478bd9Sstevel@tonic-gate 	 * must be sent back down to be decrypted properly.
1644*7c478bd9Sstevel@tonic-gate 	 *
1645*7c478bd9Sstevel@tonic-gate 	 * 'ncc' is the number of bytes that have been read but
1646*7c478bd9Sstevel@tonic-gate 	 * not yet processed by the telnet state machine.
1647*7c478bd9Sstevel@tonic-gate 	 *
1648*7c478bd9Sstevel@tonic-gate 	 * 'bytes' is the number of bytes waiting to be read from
1649*7c478bd9Sstevel@tonic-gate 	 * the stream.
1650*7c478bd9Sstevel@tonic-gate 	 *
1651*7c478bd9Sstevel@tonic-gate 	 * If either one is a positive value, then those bytes
1652*7c478bd9Sstevel@tonic-gate 	 * must be pulled up and sent back down to be decrypted.
1653*7c478bd9Sstevel@tonic-gate 	 */
1654*7c478bd9Sstevel@tonic-gate 	if (ncc || bytes) {
1655*7c478bd9Sstevel@tonic-gate 		drainstream(bytes);
1656*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1657*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1658*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_start) after drainstream, "
1659*7c478bd9Sstevel@tonic-gate 				"ncc=%d bytes = %d\n", ncc, bytes);
1660*7c478bd9Sstevel@tonic-gate 		bytes += ncc;
1661*7c478bd9Sstevel@tonic-gate 		dataptr = netip;
1662*7c478bd9Sstevel@tonic-gate 	}
1663*7c478bd9Sstevel@tonic-gate 
1664*7c478bd9Sstevel@tonic-gate 	start_stream(cryptmod_fd, CRYPT_DECRYPT, bytes, dataptr);
1665*7c478bd9Sstevel@tonic-gate 
1666*7c478bd9Sstevel@tonic-gate 	/*
1667*7c478bd9Sstevel@tonic-gate 	 * The bytes putback into the stream are no longer
1668*7c478bd9Sstevel@tonic-gate 	 * available to be read by the server, so adjust the
1669*7c478bd9Sstevel@tonic-gate 	 * counter accordingly.
1670*7c478bd9Sstevel@tonic-gate 	 */
1671*7c478bd9Sstevel@tonic-gate 	ncc = 0;
1672*7c478bd9Sstevel@tonic-gate 	netip = netibuf;
1673*7c478bd9Sstevel@tonic-gate 	(void) memset(netip, 0, netibufsize);
1674*7c478bd9Sstevel@tonic-gate 
1675*7c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
1676*7c478bd9Sstevel@tonic-gate 	if (enc_debug) {
1677*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1678*7c478bd9Sstevel@tonic-gate 			    "\t(encrypt_start) Start DECRYPT using %s\n",
1679*7c478bd9Sstevel@tonic-gate 			    ENCTYPE_NAME(encr_data.decrypt.type));
1680*7c478bd9Sstevel@tonic-gate 	}
1681*7c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
1682*7c478bd9Sstevel@tonic-gate }
1683*7c478bd9Sstevel@tonic-gate 
1684*7c478bd9Sstevel@tonic-gate /*
1685*7c478bd9Sstevel@tonic-gate  * encrypt_support
1686*7c478bd9Sstevel@tonic-gate  *
1687*7c478bd9Sstevel@tonic-gate  * Called when we recieve the TELOPT_ENCRYPT SUPPORT [ encr type list ]
1688*7c478bd9Sstevel@tonic-gate  * message from a client.
1689*7c478bd9Sstevel@tonic-gate  *
1690*7c478bd9Sstevel@tonic-gate  * Choose an agreeable method (DES_CFB64) and
1691*7c478bd9Sstevel@tonic-gate  * respond with  TELOPT_ENCRYPT ENCRYPT_IS [ desired crypto method ]
1692*7c478bd9Sstevel@tonic-gate  *
1693*7c478bd9Sstevel@tonic-gate  * from: RFC 2946
1694*7c478bd9Sstevel@tonic-gate  */
1695*7c478bd9Sstevel@tonic-gate static void
1696*7c478bd9Sstevel@tonic-gate encrypt_support(char *data, int cnt)
1697*7c478bd9Sstevel@tonic-gate {
1698*7c478bd9Sstevel@tonic-gate 	int lstate = ENCR_STATE_NOT_READY;
1699*7c478bd9Sstevel@tonic-gate 	int type, use_type = 0;
1700*7c478bd9Sstevel@tonic-gate 
1701*7c478bd9Sstevel@tonic-gate 	while (cnt-- > 0 && use_type == 0) {
1702*7c478bd9Sstevel@tonic-gate 		type = *data++;
1703*7c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
1704*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1705*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1706*7c478bd9Sstevel@tonic-gate 				    "RCVD ENCRYPT SUPPORT %s\n",
1707*7c478bd9Sstevel@tonic-gate 				    ENCTYPE_NAME(type));
1708*7c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
1709*7c478bd9Sstevel@tonic-gate 		/*
1710*7c478bd9Sstevel@tonic-gate 		 * Prefer CFB64
1711*7c478bd9Sstevel@tonic-gate 		 */
1712*7c478bd9Sstevel@tonic-gate 		if (type == TELOPT_ENCTYPE_DES_CFB64) {
1713*7c478bd9Sstevel@tonic-gate 			use_type = type;
1714*7c478bd9Sstevel@tonic-gate 		}
1715*7c478bd9Sstevel@tonic-gate 	}
1716*7c478bd9Sstevel@tonic-gate 	encr_data.encrypt.type = use_type;
1717*7c478bd9Sstevel@tonic-gate 
1718*7c478bd9Sstevel@tonic-gate 	if (use_type != TELOPT_ENCTYPE_NULL &&
1719*7c478bd9Sstevel@tonic-gate 	    authenticated != NULL && authenticated != &NoAuth &&
1720*7c478bd9Sstevel@tonic-gate 	    auth_status != AUTH_REJECT) {
1721*7c478bd9Sstevel@tonic-gate 
1722*7c478bd9Sstevel@tonic-gate 		/* Authenticated -> have session key -> send ENCRYPT IS */
1723*7c478bd9Sstevel@tonic-gate 		lstate = encrypt_send_encrypt_is();
1724*7c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_OK)
1725*7c478bd9Sstevel@tonic-gate 			encrypt_start_output();
1726*7c478bd9Sstevel@tonic-gate 	} else if (use_type == TELOPT_ENCTYPE_NULL) {
1727*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1728*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1729*7c478bd9Sstevel@tonic-gate 				    "\t(encrypt_support) Cannot agree "
1730*7c478bd9Sstevel@tonic-gate 				    "on crypto algorithm, output encryption "
1731*7c478bd9Sstevel@tonic-gate 				    "disabled.\n");
1732*7c478bd9Sstevel@tonic-gate 
1733*7c478bd9Sstevel@tonic-gate 		/*
1734*7c478bd9Sstevel@tonic-gate 		 * Cannot agree on crypto algorithm
1735*7c478bd9Sstevel@tonic-gate 		 * RFC 2946 sez:
1736*7c478bd9Sstevel@tonic-gate 		 *    send "IAC SB ENCRYPT IS NULL IAC SE"
1737*7c478bd9Sstevel@tonic-gate 		 *    optionally, also send IAC WONT ENCRYPT
1738*7c478bd9Sstevel@tonic-gate 		 */
1739*7c478bd9Sstevel@tonic-gate 		write_data("%c%c%c%c%c%c%c",
1740*7c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
1741*7c478bd9Sstevel@tonic-gate 			(uchar_t)SB,
1742*7c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCRYPT,
1743*7c478bd9Sstevel@tonic-gate 			(uchar_t)ENCRYPT_IS,
1744*7c478bd9Sstevel@tonic-gate 			(uchar_t)TELOPT_ENCTYPE_NULL,
1745*7c478bd9Sstevel@tonic-gate 			(uchar_t)IAC,
1746*7c478bd9Sstevel@tonic-gate 			(uchar_t)SE);
1747*7c478bd9Sstevel@tonic-gate 		send_wont(TELOPT_ENCRYPT);
1748*7c478bd9Sstevel@tonic-gate 		netflush();
1749*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1750*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1751*7c478bd9Sstevel@tonic-gate 				    "SENT TELOPT_ENCRYPT ENCRYPT_IS "
1752*7c478bd9Sstevel@tonic-gate 				    "[NULL]\n");
1753*7c478bd9Sstevel@tonic-gate 
1754*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] = OPT_NO;
1755*7c478bd9Sstevel@tonic-gate 	}
1756*7c478bd9Sstevel@tonic-gate 	settimer(encr_support);
1757*7c478bd9Sstevel@tonic-gate }
1758*7c478bd9Sstevel@tonic-gate 
1759*7c478bd9Sstevel@tonic-gate /*
1760*7c478bd9Sstevel@tonic-gate  * encrypt_send_keyid
1761*7c478bd9Sstevel@tonic-gate  *
1762*7c478bd9Sstevel@tonic-gate  * Sent the key id we will use to the client
1763*7c478bd9Sstevel@tonic-gate  */
1764*7c478bd9Sstevel@tonic-gate static void
1765*7c478bd9Sstevel@tonic-gate encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, boolean_t saveit)
1766*7c478bd9Sstevel@tonic-gate {
1767*7c478bd9Sstevel@tonic-gate 	uchar_t sbbuf[128], *p;
1768*7c478bd9Sstevel@tonic-gate 
1769*7c478bd9Sstevel@tonic-gate 	p = sbbuf;
1770*7c478bd9Sstevel@tonic-gate 
1771*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1772*7c478bd9Sstevel@tonic-gate 	*p++ = SB;
1773*7c478bd9Sstevel@tonic-gate 	*p++ = TELOPT_ENCRYPT;
1774*7c478bd9Sstevel@tonic-gate 	*p++ = (dir == TELNET_DIR_ENCRYPT ? ENCRYPT_ENC_KEYID :
1775*7c478bd9Sstevel@tonic-gate 		ENCRYPT_DEC_KEYID);
1776*7c478bd9Sstevel@tonic-gate 	if (saveit) {
1777*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1778*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1779*7c478bd9Sstevel@tonic-gate 				"\t(send_keyid) store %d byte %s keyid\n",
1780*7c478bd9Sstevel@tonic-gate 				keylen,
1781*7c478bd9Sstevel@tonic-gate 				(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
1782*7c478bd9Sstevel@tonic-gate 				"DECRYPT"));
1783*7c478bd9Sstevel@tonic-gate 
1784*7c478bd9Sstevel@tonic-gate 		if (dir == TELNET_DIR_ENCRYPT) {
1785*7c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.encrypt.keyid, keyid, keylen);
1786*7c478bd9Sstevel@tonic-gate 			encr_data.encrypt.keyidlen = keylen;
1787*7c478bd9Sstevel@tonic-gate 		} else {
1788*7c478bd9Sstevel@tonic-gate 			(void) memcpy(encr_data.decrypt.keyid, keyid, keylen);
1789*7c478bd9Sstevel@tonic-gate 			encr_data.decrypt.keyidlen = keylen;
1790*7c478bd9Sstevel@tonic-gate 		}
1791*7c478bd9Sstevel@tonic-gate 	}
1792*7c478bd9Sstevel@tonic-gate 	(void) memcpy(p, keyid, keylen);
1793*7c478bd9Sstevel@tonic-gate 	p += keylen;
1794*7c478bd9Sstevel@tonic-gate 
1795*7c478bd9Sstevel@tonic-gate 	*p++ = IAC;
1796*7c478bd9Sstevel@tonic-gate 	*p++ = SE;
1797*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
1798*7c478bd9Sstevel@tonic-gate 	netflush();
1799*7c478bd9Sstevel@tonic-gate 
1800*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1801*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "SENT TELOPT_ENCRYPT %s %d\n",
1802*7c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? "ENC_KEYID" :
1803*7c478bd9Sstevel@tonic-gate 			"DEC_KEYID"), keyid[0]);
1804*7c478bd9Sstevel@tonic-gate }
1805*7c478bd9Sstevel@tonic-gate 
1806*7c478bd9Sstevel@tonic-gate /*
1807*7c478bd9Sstevel@tonic-gate  * encrypt_reply
1808*7c478bd9Sstevel@tonic-gate  *
1809*7c478bd9Sstevel@tonic-gate  * When we receive the TELOPT_ENCRYPT REPLY [crtype] CFB64_IV_OK IAC SE
1810*7c478bd9Sstevel@tonic-gate  * message, process it accordingly.
1811*7c478bd9Sstevel@tonic-gate  * If the vector is acceptable, tell client we are encrypting and
1812*7c478bd9Sstevel@tonic-gate  * enable encryption on our write stream.
1813*7c478bd9Sstevel@tonic-gate  *
1814*7c478bd9Sstevel@tonic-gate  * Negotiate the KEYID next..
1815*7c478bd9Sstevel@tonic-gate  * RFC 2946, 2952
1816*7c478bd9Sstevel@tonic-gate  */
1817*7c478bd9Sstevel@tonic-gate static void
1818*7c478bd9Sstevel@tonic-gate encrypt_reply(char *data, int len)
1819*7c478bd9Sstevel@tonic-gate {
1820*7c478bd9Sstevel@tonic-gate 	uchar_t type = (uchar_t)(*data++);
1821*7c478bd9Sstevel@tonic-gate 	uchar_t result = (uchar_t)(*data);
1822*7c478bd9Sstevel@tonic-gate 	int lstate;
1823*7c478bd9Sstevel@tonic-gate 
1824*7c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
1825*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1826*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1827*7c478bd9Sstevel@tonic-gate 			"\t(encrypt_reply) ENCRYPT REPLY %s %s [len=%d]\n",
1828*7c478bd9Sstevel@tonic-gate 			ENCRYPT_NAME(type),
1829*7c478bd9Sstevel@tonic-gate 			(result == CFB64_IV_OK ? "CFB64_IV_OK" :
1830*7c478bd9Sstevel@tonic-gate 			"CFB64_IV_BAD"), len);
1831*7c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
1832*7c478bd9Sstevel@tonic-gate 
1833*7c478bd9Sstevel@tonic-gate 	lstate = encr_data.encrypt.state;
1834*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1835*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1836*7c478bd9Sstevel@tonic-gate 			"\t(encrypt_reply) initial ENCRYPT state = %d\n",
1837*7c478bd9Sstevel@tonic-gate 			lstate);
1838*7c478bd9Sstevel@tonic-gate 	switch (result) {
1839*7c478bd9Sstevel@tonic-gate 	case CFB64_IV_OK:
1840*7c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_NOT_READY)
1841*7c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
1842*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_RECV_IV; /* we got the IV */
1843*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont need to send IV */
1844*7c478bd9Sstevel@tonic-gate 
1845*7c478bd9Sstevel@tonic-gate 		/*
1846*7c478bd9Sstevel@tonic-gate 		 * The correct response here is to send the encryption key id
1847*7c478bd9Sstevel@tonic-gate 		 * RFC 2752.
1848*7c478bd9Sstevel@tonic-gate 		 *
1849*7c478bd9Sstevel@tonic-gate 		 * Send keyid 0 to indicate that we will just use default
1850*7c478bd9Sstevel@tonic-gate 		 * keys.
1851*7c478bd9Sstevel@tonic-gate 		 */
1852*7c478bd9Sstevel@tonic-gate 		encrypt_send_keyid(TELNET_DIR_ENCRYPT, (uchar_t *)"\0", 1, 1);
1853*7c478bd9Sstevel@tonic-gate 
1854*7c478bd9Sstevel@tonic-gate 		break;
1855*7c478bd9Sstevel@tonic-gate 	case CFB64_IV_BAD:
1856*7c478bd9Sstevel@tonic-gate 		/*
1857*7c478bd9Sstevel@tonic-gate 		 * Clear the ivec
1858*7c478bd9Sstevel@tonic-gate 		 */
1859*7c478bd9Sstevel@tonic-gate 		(void) memset(encr_data.encrypt.ivec, 0, sizeof (Block));
1860*7c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
1861*7c478bd9Sstevel@tonic-gate 		break;
1862*7c478bd9Sstevel@tonic-gate 	default:
1863*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1864*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1865*7c478bd9Sstevel@tonic-gate 				"\t(encrypt_reply) Got unknown IV value in "
1866*7c478bd9Sstevel@tonic-gate 				"REPLY message\n");
1867*7c478bd9Sstevel@tonic-gate 		lstate = ENCR_STATE_NOT_READY;
1868*7c478bd9Sstevel@tonic-gate 		break;
1869*7c478bd9Sstevel@tonic-gate 	}
1870*7c478bd9Sstevel@tonic-gate 
1871*7c478bd9Sstevel@tonic-gate 	encr_data.encrypt.state = lstate;
1872*7c478bd9Sstevel@tonic-gate 	if (lstate == ENCR_STATE_NOT_READY) {
1873*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag = 0;
1874*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = ENCTYPE_NULL;
1875*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1876*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1877*7c478bd9Sstevel@tonic-gate 				    "\t(encrypt_reply) encrypt.autoflag = "
1878*7c478bd9Sstevel@tonic-gate 				    "OFF\n");
1879*7c478bd9Sstevel@tonic-gate 	} else {
1880*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.type = type;
1881*7c478bd9Sstevel@tonic-gate 		if ((lstate == ENCR_STATE_OK) && encr_data.encrypt.autoflag)
1882*7c478bd9Sstevel@tonic-gate 			encrypt_start_output();
1883*7c478bd9Sstevel@tonic-gate 	}
1884*7c478bd9Sstevel@tonic-gate 
1885*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1886*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1887*7c478bd9Sstevel@tonic-gate 			    "\t(encrypt_reply) ENCRYPT final state = %d\n",
1888*7c478bd9Sstevel@tonic-gate 			    lstate);
1889*7c478bd9Sstevel@tonic-gate }
1890*7c478bd9Sstevel@tonic-gate 
1891*7c478bd9Sstevel@tonic-gate static void
1892*7c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(uchar_t *keyid, int *keyidlen, int dir)
1893*7c478bd9Sstevel@tonic-gate {
1894*7c478bd9Sstevel@tonic-gate 	int lstate;
1895*7c478bd9Sstevel@tonic-gate 
1896*7c478bd9Sstevel@tonic-gate 	lstate = (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state :
1897*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.state);
1898*7c478bd9Sstevel@tonic-gate 
1899*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1900*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1901*7c478bd9Sstevel@tonic-gate 			    "\t(set_keyid_state) %s initial state = %d\n",
1902*7c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
1903*7c478bd9Sstevel@tonic-gate 			    "DECRYPT"), lstate);
1904*7c478bd9Sstevel@tonic-gate 
1905*7c478bd9Sstevel@tonic-gate 	/*
1906*7c478bd9Sstevel@tonic-gate 	 * Currently, we only support using the default keyid,
1907*7c478bd9Sstevel@tonic-gate 	 * so it should be an error if the len > 1 or the keyid != 0.
1908*7c478bd9Sstevel@tonic-gate 	 */
1909*7c478bd9Sstevel@tonic-gate 	if (*keyidlen != 1 || (*keyid != '\0')) {
1910*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1911*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1912*7c478bd9Sstevel@tonic-gate 				    "\t(set_keyid_state) unexpected keyid: "
1913*7c478bd9Sstevel@tonic-gate 				    "len=%d value=%d\n", *keyidlen, *keyid);
1914*7c478bd9Sstevel@tonic-gate 		*keyidlen = 0;
1915*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "rcvd unexpected keyid %d  - only keyid of 0 "
1916*7c478bd9Sstevel@tonic-gate 		    "is supported",  *keyid);
1917*7c478bd9Sstevel@tonic-gate 	} else {
1918*7c478bd9Sstevel@tonic-gate 		/*
1919*7c478bd9Sstevel@tonic-gate 		 * We move to the "IN_PROGRESS" state.
1920*7c478bd9Sstevel@tonic-gate 		 */
1921*7c478bd9Sstevel@tonic-gate 		if (lstate == ENCR_STATE_NOT_READY)
1922*7c478bd9Sstevel@tonic-gate 			lstate = ENCR_STATE_IN_PROGRESS;
1923*7c478bd9Sstevel@tonic-gate 		/*
1924*7c478bd9Sstevel@tonic-gate 		 * Clear the NO_KEYID bit because we now have a valid keyid
1925*7c478bd9Sstevel@tonic-gate 		 */
1926*7c478bd9Sstevel@tonic-gate 		lstate &= ~ENCR_STATE_NO_KEYID;
1927*7c478bd9Sstevel@tonic-gate 	}
1928*7c478bd9Sstevel@tonic-gate 
1929*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
1930*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1931*7c478bd9Sstevel@tonic-gate 			    "\t(set_keyid_state) %s final state = %d\n",
1932*7c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
1933*7c478bd9Sstevel@tonic-gate 			    "DECRYPT"), lstate);
1934*7c478bd9Sstevel@tonic-gate 
1935*7c478bd9Sstevel@tonic-gate 	if (dir == TELNET_DIR_ENCRYPT)
1936*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.state = lstate;
1937*7c478bd9Sstevel@tonic-gate 	else
1938*7c478bd9Sstevel@tonic-gate 		encr_data.decrypt.state = lstate;
1939*7c478bd9Sstevel@tonic-gate }
1940*7c478bd9Sstevel@tonic-gate 
1941*7c478bd9Sstevel@tonic-gate /*
1942*7c478bd9Sstevel@tonic-gate  * encrypt_keyid
1943*7c478bd9Sstevel@tonic-gate  *
1944*7c478bd9Sstevel@tonic-gate  * Set the keyid value in the key_info structure.
1945*7c478bd9Sstevel@tonic-gate  * if necessary send a response to the sender
1946*7c478bd9Sstevel@tonic-gate  */
1947*7c478bd9Sstevel@tonic-gate static void
1948*7c478bd9Sstevel@tonic-gate encrypt_keyid(uchar_t *newkeyid, int *keyidlen, uchar_t *keyid,
1949*7c478bd9Sstevel@tonic-gate 	int len, int dir)
1950*7c478bd9Sstevel@tonic-gate {
1951*7c478bd9Sstevel@tonic-gate 	if (len > TELNET_MAXNUMKEYS) {
1952*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1953*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1954*7c478bd9Sstevel@tonic-gate 				    "\t(keyid) keylen too big (%d)\n", len);
1955*7c478bd9Sstevel@tonic-gate 		return;
1956*7c478bd9Sstevel@tonic-gate 	}
1957*7c478bd9Sstevel@tonic-gate 
1958*7c478bd9Sstevel@tonic-gate 	if (enc_debug) {
1959*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(keyid) set KEYID for %s len = %d\n",
1960*7c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
1961*7c478bd9Sstevel@tonic-gate 			    "DECRYPT"), len);
1962*7c478bd9Sstevel@tonic-gate 	}
1963*7c478bd9Sstevel@tonic-gate 
1964*7c478bd9Sstevel@tonic-gate 	if (len == 0) {
1965*7c478bd9Sstevel@tonic-gate 		if (*keyidlen == 0) {
1966*7c478bd9Sstevel@tonic-gate 			if (enc_debug)
1967*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
1968*7c478bd9Sstevel@tonic-gate 					    "\t(keyid) Got 0 length keyid - "
1969*7c478bd9Sstevel@tonic-gate 					    "failure\n");
1970*7c478bd9Sstevel@tonic-gate 			return;
1971*7c478bd9Sstevel@tonic-gate 		}
1972*7c478bd9Sstevel@tonic-gate 		*keyidlen = 0;
1973*7c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
1974*7c478bd9Sstevel@tonic-gate 
1975*7c478bd9Sstevel@tonic-gate 	} else if (len != *keyidlen || memcmp(keyid, newkeyid, len)) {
1976*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1977*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1978*7c478bd9Sstevel@tonic-gate 				    "\t(keyid) Setting new key (%d bytes)\n",
1979*7c478bd9Sstevel@tonic-gate 				    len);
1980*7c478bd9Sstevel@tonic-gate 
1981*7c478bd9Sstevel@tonic-gate 		*keyidlen = len;
1982*7c478bd9Sstevel@tonic-gate 		(void) memcpy(newkeyid, keyid, len);
1983*7c478bd9Sstevel@tonic-gate 
1984*7c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
1985*7c478bd9Sstevel@tonic-gate 	} else {
1986*7c478bd9Sstevel@tonic-gate 		encrypt_set_keyid_state(newkeyid, keyidlen, dir);
1987*7c478bd9Sstevel@tonic-gate 
1988*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
1989*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
1990*7c478bd9Sstevel@tonic-gate 				    "\t(keyid) %s Key already in place,"
1991*7c478bd9Sstevel@tonic-gate 				    "state = %d autoflag=%d\n",
1992*7c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" : "DECRYPT"),
1993*7c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state:
1994*7c478bd9Sstevel@tonic-gate 			encr_data.decrypt.state),
1995*7c478bd9Sstevel@tonic-gate 			(dir == TELNET_DIR_ENCRYPT ?
1996*7c478bd9Sstevel@tonic-gate 				encr_data.encrypt.autoflag:
1997*7c478bd9Sstevel@tonic-gate 				encr_data.decrypt.autoflag));
1998*7c478bd9Sstevel@tonic-gate 
1999*7c478bd9Sstevel@tonic-gate 		/* key already in place */
2000*7c478bd9Sstevel@tonic-gate 		if ((encr_data.encrypt.state == ENCR_STATE_OK) &&
2001*7c478bd9Sstevel@tonic-gate 		    dir == TELNET_DIR_ENCRYPT && encr_data.encrypt.autoflag) {
2002*7c478bd9Sstevel@tonic-gate 			encrypt_start_output();
2003*7c478bd9Sstevel@tonic-gate 		}
2004*7c478bd9Sstevel@tonic-gate 		return;
2005*7c478bd9Sstevel@tonic-gate 	}
2006*7c478bd9Sstevel@tonic-gate 
2007*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
2008*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\t(keyid) %s final state = %d\n",
2009*7c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
2010*7c478bd9Sstevel@tonic-gate 			    "DECRYPT"),
2011*7c478bd9Sstevel@tonic-gate 			    (dir == TELNET_DIR_ENCRYPT ?
2012*7c478bd9Sstevel@tonic-gate 			    encr_data.encrypt.state :
2013*7c478bd9Sstevel@tonic-gate 			    encr_data.decrypt.state));
2014*7c478bd9Sstevel@tonic-gate 
2015*7c478bd9Sstevel@tonic-gate 	encrypt_send_keyid(dir, newkeyid, *keyidlen, 0);
2016*7c478bd9Sstevel@tonic-gate }
2017*7c478bd9Sstevel@tonic-gate 
2018*7c478bd9Sstevel@tonic-gate /*
2019*7c478bd9Sstevel@tonic-gate  * encrypt_enc_keyid
2020*7c478bd9Sstevel@tonic-gate  *
2021*7c478bd9Sstevel@tonic-gate  * We received the ENC_KEYID message from a client indicating that
2022*7c478bd9Sstevel@tonic-gate  * the client wishes to verify that the indicated keyid maps to a
2023*7c478bd9Sstevel@tonic-gate  * valid key.
2024*7c478bd9Sstevel@tonic-gate  */
2025*7c478bd9Sstevel@tonic-gate static void
2026*7c478bd9Sstevel@tonic-gate encrypt_enc_keyid(char *data, int cnt)
2027*7c478bd9Sstevel@tonic-gate {
2028*7c478bd9Sstevel@tonic-gate 	/*
2029*7c478bd9Sstevel@tonic-gate 	 * Verify the decrypt keyid is valid
2030*7c478bd9Sstevel@tonic-gate 	 */
2031*7c478bd9Sstevel@tonic-gate 	encrypt_keyid(encr_data.decrypt.keyid, &encr_data.decrypt.keyidlen,
2032*7c478bd9Sstevel@tonic-gate 		    (uchar_t *)data, cnt, TELNET_DIR_DECRYPT);
2033*7c478bd9Sstevel@tonic-gate }
2034*7c478bd9Sstevel@tonic-gate 
2035*7c478bd9Sstevel@tonic-gate /*
2036*7c478bd9Sstevel@tonic-gate  * encrypt_dec_keyid
2037*7c478bd9Sstevel@tonic-gate  *
2038*7c478bd9Sstevel@tonic-gate  * We received the DEC_KEYID message from a client indicating that
2039*7c478bd9Sstevel@tonic-gate  * the client wants to verify that the indicated keyid maps to a valid key.
2040*7c478bd9Sstevel@tonic-gate  */
2041*7c478bd9Sstevel@tonic-gate static void
2042*7c478bd9Sstevel@tonic-gate encrypt_dec_keyid(char *data, int cnt)
2043*7c478bd9Sstevel@tonic-gate {
2044*7c478bd9Sstevel@tonic-gate 	encrypt_keyid(encr_data.encrypt.keyid, &encr_data.encrypt.keyidlen,
2045*7c478bd9Sstevel@tonic-gate 		    (uchar_t *)data, cnt, TELNET_DIR_ENCRYPT);
2046*7c478bd9Sstevel@tonic-gate }
2047*7c478bd9Sstevel@tonic-gate 
2048*7c478bd9Sstevel@tonic-gate /*
2049*7c478bd9Sstevel@tonic-gate  * encrypt_session_key
2050*7c478bd9Sstevel@tonic-gate  *
2051*7c478bd9Sstevel@tonic-gate  * Store the session key in the encryption data record
2052*7c478bd9Sstevel@tonic-gate  */
2053*7c478bd9Sstevel@tonic-gate static void
2054*7c478bd9Sstevel@tonic-gate encrypt_session_key(Session_Key *key, cipher_info_t *cinfo)
2055*7c478bd9Sstevel@tonic-gate {
2056*7c478bd9Sstevel@tonic-gate 	if (key == NULL || key->type != SK_DES) {
2057*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
2058*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
2059*7c478bd9Sstevel@tonic-gate 				    "\t(session_key) Cannot set krb5 "
2060*7c478bd9Sstevel@tonic-gate 				    "session key (unknown type = %d)\n",
2061*7c478bd9Sstevel@tonic-gate 				    key ? key->type : -1);
2062*7c478bd9Sstevel@tonic-gate 	}
2063*7c478bd9Sstevel@tonic-gate 	if (enc_debug)
2064*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
2065*7c478bd9Sstevel@tonic-gate 			    "\t(session_key) Settting session key "
2066*7c478bd9Sstevel@tonic-gate 			    "for server\n");
2067*7c478bd9Sstevel@tonic-gate 
2068*7c478bd9Sstevel@tonic-gate 	/* store the key in the cipher info data struct */
2069*7c478bd9Sstevel@tonic-gate 	(void) memcpy(cinfo->krbdes_key, (void *)key->data, sizeof (Block));
2070*7c478bd9Sstevel@tonic-gate 
2071*7c478bd9Sstevel@tonic-gate 	/*
2072*7c478bd9Sstevel@tonic-gate 	 * Now look to see if we still need to send the key and start
2073*7c478bd9Sstevel@tonic-gate 	 * encrypting.
2074*7c478bd9Sstevel@tonic-gate 	 *
2075*7c478bd9Sstevel@tonic-gate 	 * If so, go ahead an call it now that we have the key.
2076*7c478bd9Sstevel@tonic-gate 	 */
2077*7c478bd9Sstevel@tonic-gate 	if (cinfo->need_start) {
2078*7c478bd9Sstevel@tonic-gate 		if (encrypt_send_encrypt_is() == ENCR_STATE_OK) {
2079*7c478bd9Sstevel@tonic-gate 			cinfo->need_start = 0;
2080*7c478bd9Sstevel@tonic-gate 		}
2081*7c478bd9Sstevel@tonic-gate 	}
2082*7c478bd9Sstevel@tonic-gate }
2083*7c478bd9Sstevel@tonic-gate 
2084*7c478bd9Sstevel@tonic-gate /*
2085*7c478bd9Sstevel@tonic-gate  * new_env
2086*7c478bd9Sstevel@tonic-gate  *
2087*7c478bd9Sstevel@tonic-gate  * Used to add an environment variable and value to the
2088*7c478bd9Sstevel@tonic-gate  * linked list structure.
2089*7c478bd9Sstevel@tonic-gate  */
2090*7c478bd9Sstevel@tonic-gate static int
2091*7c478bd9Sstevel@tonic-gate new_env(const char *name, const char *value)
2092*7c478bd9Sstevel@tonic-gate {
2093*7c478bd9Sstevel@tonic-gate 	struct envlist *env;
2094*7c478bd9Sstevel@tonic-gate 
2095*7c478bd9Sstevel@tonic-gate 	env = malloc(sizeof (struct envlist));
2096*7c478bd9Sstevel@tonic-gate 	if (env == NULL)
2097*7c478bd9Sstevel@tonic-gate 		return (1);
2098*7c478bd9Sstevel@tonic-gate 	if ((env->name = strdup(name)) == NULL) {
2099*7c478bd9Sstevel@tonic-gate 		free(env);
2100*7c478bd9Sstevel@tonic-gate 		return (1);
2101*7c478bd9Sstevel@tonic-gate 	}
2102*7c478bd9Sstevel@tonic-gate 	if ((env->value = strdup(value)) == NULL) {
2103*7c478bd9Sstevel@tonic-gate 		free(env->name);
2104*7c478bd9Sstevel@tonic-gate 		free(env);
2105*7c478bd9Sstevel@tonic-gate 		return (1);
2106*7c478bd9Sstevel@tonic-gate 	}
2107*7c478bd9Sstevel@tonic-gate 	env->delete = 0;
2108*7c478bd9Sstevel@tonic-gate 	env->next = envlist_head;
2109*7c478bd9Sstevel@tonic-gate 	envlist_head = env;
2110*7c478bd9Sstevel@tonic-gate 	return (0);
2111*7c478bd9Sstevel@tonic-gate }
2112*7c478bd9Sstevel@tonic-gate 
2113*7c478bd9Sstevel@tonic-gate /*
2114*7c478bd9Sstevel@tonic-gate  * del_env
2115*7c478bd9Sstevel@tonic-gate  *
2116*7c478bd9Sstevel@tonic-gate  * Used to delete an environment variable from the linked list
2117*7c478bd9Sstevel@tonic-gate  * structure.  We just set a flag because we will delete the list
2118*7c478bd9Sstevel@tonic-gate  * anyway before we exec login.
2119*7c478bd9Sstevel@tonic-gate  */
2120*7c478bd9Sstevel@tonic-gate static int
2121*7c478bd9Sstevel@tonic-gate del_env(const char *name)
2122*7c478bd9Sstevel@tonic-gate {
2123*7c478bd9Sstevel@tonic-gate 	struct envlist *env;
2124*7c478bd9Sstevel@tonic-gate 
2125*7c478bd9Sstevel@tonic-gate 	for (env = envlist_head; env; env = env->next) {
2126*7c478bd9Sstevel@tonic-gate 		if (strcmp(env->name, name) == 0) {
2127*7c478bd9Sstevel@tonic-gate 			env->delete = 1;
2128*7c478bd9Sstevel@tonic-gate 			break;
2129*7c478bd9Sstevel@tonic-gate 		}
2130*7c478bd9Sstevel@tonic-gate 	}
2131*7c478bd9Sstevel@tonic-gate 	return (0);
2132*7c478bd9Sstevel@tonic-gate }
2133*7c478bd9Sstevel@tonic-gate 
2134*7c478bd9Sstevel@tonic-gate static int
2135*7c478bd9Sstevel@tonic-gate issock(int fd)
2136*7c478bd9Sstevel@tonic-gate {
2137*7c478bd9Sstevel@tonic-gate 	struct stat stats;
2138*7c478bd9Sstevel@tonic-gate 
2139*7c478bd9Sstevel@tonic-gate 	if (fstat(fd, &stats) == -1)
2140*7c478bd9Sstevel@tonic-gate 		return (0);
2141*7c478bd9Sstevel@tonic-gate 	return (S_ISSOCK(stats.st_mode));
2142*7c478bd9Sstevel@tonic-gate }
2143*7c478bd9Sstevel@tonic-gate 
2144*7c478bd9Sstevel@tonic-gate /*
2145*7c478bd9Sstevel@tonic-gate  * audit_telnet_settid stores the terminal id while it is still
2146*7c478bd9Sstevel@tonic-gate  * available.  Subsequent calls to adt_load_hostname() return
2147*7c478bd9Sstevel@tonic-gate  * the id which is stored here.
2148*7c478bd9Sstevel@tonic-gate  */
2149*7c478bd9Sstevel@tonic-gate static int
2150*7c478bd9Sstevel@tonic-gate audit_telnet_settid(int sock) {
2151*7c478bd9Sstevel@tonic-gate 	adt_session_data_t	*ah;
2152*7c478bd9Sstevel@tonic-gate 	adt_termid_t		*termid;
2153*7c478bd9Sstevel@tonic-gate 	int			rc;
2154*7c478bd9Sstevel@tonic-gate 
2155*7c478bd9Sstevel@tonic-gate 	if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
2156*7c478bd9Sstevel@tonic-gate 		if ((rc = adt_load_termid(sock, &termid)) == 0) {
2157*7c478bd9Sstevel@tonic-gate 			if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
2158*7c478bd9Sstevel@tonic-gate 			    ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
2159*7c478bd9Sstevel@tonic-gate 			    termid, ADT_SETTID)) == 0)
2160*7c478bd9Sstevel@tonic-gate 				(void) adt_set_proc(ah);
2161*7c478bd9Sstevel@tonic-gate 			free(termid);
2162*7c478bd9Sstevel@tonic-gate 		}
2163*7c478bd9Sstevel@tonic-gate 		(void) adt_end_session(ah);
2164*7c478bd9Sstevel@tonic-gate 	}
2165*7c478bd9Sstevel@tonic-gate 	return (rc);
2166*7c478bd9Sstevel@tonic-gate }
2167*7c478bd9Sstevel@tonic-gate 
2168*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
2169*7c478bd9Sstevel@tonic-gate int
2170*7c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
2171*7c478bd9Sstevel@tonic-gate {
2172*7c478bd9Sstevel@tonic-gate 	struct sockaddr_storage from;
2173*7c478bd9Sstevel@tonic-gate 	int on = 1;
2174*7c478bd9Sstevel@tonic-gate 	socklen_t fromlen;
2175*7c478bd9Sstevel@tonic-gate 	int issocket;
2176*7c478bd9Sstevel@tonic-gate #if	defined(DEBUG)
2177*7c478bd9Sstevel@tonic-gate 	ushort_t porttouse = 0;
2178*7c478bd9Sstevel@tonic-gate 	boolean_t standalone = 0;
2179*7c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
2180*7c478bd9Sstevel@tonic-gate 	extern char *optarg;
2181*7c478bd9Sstevel@tonic-gate 	char c;
2182*7c478bd9Sstevel@tonic-gate 	int tos = -1;
2183*7c478bd9Sstevel@tonic-gate 
2184*7c478bd9Sstevel@tonic-gate 	while ((c = getopt(argc, argv, TELNETD_OPTS DEBUG_OPTS)) != -1) {
2185*7c478bd9Sstevel@tonic-gate 		switch (c) {
2186*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
2187*7c478bd9Sstevel@tonic-gate 		case 'p':
2188*7c478bd9Sstevel@tonic-gate 			/*
2189*7c478bd9Sstevel@tonic-gate 			 * note: alternative port number only used in
2190*7c478bd9Sstevel@tonic-gate 			 * standalone mode.
2191*7c478bd9Sstevel@tonic-gate 			 */
2192*7c478bd9Sstevel@tonic-gate 			porttouse = atoi(optarg);
2193*7c478bd9Sstevel@tonic-gate 			standalone = 1;
2194*7c478bd9Sstevel@tonic-gate 			break;
2195*7c478bd9Sstevel@tonic-gate 		case 'e':
2196*7c478bd9Sstevel@tonic-gate 			enc_debug = 1;
2197*7c478bd9Sstevel@tonic-gate 			break;
2198*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2199*7c478bd9Sstevel@tonic-gate 		case 'a':
2200*7c478bd9Sstevel@tonic-gate 			if (strcasecmp(optarg, "none") == 0) {
2201*7c478bd9Sstevel@tonic-gate 				auth_level = 0;
2202*7c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "user") == 0) {
2203*7c478bd9Sstevel@tonic-gate 				auth_level = AUTH_USER;
2204*7c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "valid") == 0) {
2205*7c478bd9Sstevel@tonic-gate 				auth_level = AUTH_VALID;
2206*7c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "off") == 0) {
2207*7c478bd9Sstevel@tonic-gate 				auth_level = -1;
2208*7c478bd9Sstevel@tonic-gate 				negotiate_auth_krb5 = 0;
2209*7c478bd9Sstevel@tonic-gate 			} else if (strcasecmp(optarg, "debug") == 0) {
2210*7c478bd9Sstevel@tonic-gate 				auth_debug = 1;
2211*7c478bd9Sstevel@tonic-gate 			} else {
2212*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR,
2213*7c478bd9Sstevel@tonic-gate 				    "unknown authentication level specified "
2214*7c478bd9Sstevel@tonic-gate 				    "with \'-a\' option (%s)", optarg);
2215*7c478bd9Sstevel@tonic-gate 				auth_level = AUTH_USER;
2216*7c478bd9Sstevel@tonic-gate 			}
2217*7c478bd9Sstevel@tonic-gate 			break;
2218*7c478bd9Sstevel@tonic-gate 		case 'X':
2219*7c478bd9Sstevel@tonic-gate 			/* disable authentication negotiation */
2220*7c478bd9Sstevel@tonic-gate 			negotiate_auth_krb5 = 0;
2221*7c478bd9Sstevel@tonic-gate 			break;
2222*7c478bd9Sstevel@tonic-gate 		case 'R':
2223*7c478bd9Sstevel@tonic-gate 		case 'M':
2224*7c478bd9Sstevel@tonic-gate 			if (optarg != NULL) {
2225*7c478bd9Sstevel@tonic-gate 				int ret = krb5_init();
2226*7c478bd9Sstevel@tonic-gate 				if (ret) {
2227*7c478bd9Sstevel@tonic-gate 					syslog(LOG_ERR,
2228*7c478bd9Sstevel@tonic-gate 						"Unable to use Kerberos V5 as "
2229*7c478bd9Sstevel@tonic-gate 						"requested, exiting");
2230*7c478bd9Sstevel@tonic-gate 					exit(1);
2231*7c478bd9Sstevel@tonic-gate 				}
2232*7c478bd9Sstevel@tonic-gate 				krb5_set_default_realm(telnet_context, optarg);
2233*7c478bd9Sstevel@tonic-gate 				syslog(LOG_NOTICE,
2234*7c478bd9Sstevel@tonic-gate 				    "using %s as default KRB5 realm", optarg);
2235*7c478bd9Sstevel@tonic-gate 			}
2236*7c478bd9Sstevel@tonic-gate 			break;
2237*7c478bd9Sstevel@tonic-gate 		case 'S':
2238*7c478bd9Sstevel@tonic-gate 			telnet_srvtab = (char *)strdup(optarg);
2239*7c478bd9Sstevel@tonic-gate 			break;
2240*7c478bd9Sstevel@tonic-gate 		case 'E': /* disable automatic encryption */
2241*7c478bd9Sstevel@tonic-gate 			negotiate_encrypt = B_FALSE;
2242*7c478bd9Sstevel@tonic-gate 			break;
2243*7c478bd9Sstevel@tonic-gate 		case 'U':
2244*7c478bd9Sstevel@tonic-gate 			resolve_hostname = 1;
2245*7c478bd9Sstevel@tonic-gate 			break;
2246*7c478bd9Sstevel@tonic-gate 		case 's':
2247*7c478bd9Sstevel@tonic-gate 			if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
2248*7c478bd9Sstevel@tonic-gate 			    tos > 255) {
2249*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR, "telnetd: illegal tos value: "
2250*7c478bd9Sstevel@tonic-gate 				    "%s\n", optarg);
2251*7c478bd9Sstevel@tonic-gate 			} else {
2252*7c478bd9Sstevel@tonic-gate 				if (tos < 0)
2253*7c478bd9Sstevel@tonic-gate 					tos = 020;
2254*7c478bd9Sstevel@tonic-gate 			}
2255*7c478bd9Sstevel@tonic-gate 			break;
2256*7c478bd9Sstevel@tonic-gate 		case 'h':
2257*7c478bd9Sstevel@tonic-gate 			show_hostinfo = 0;
2258*7c478bd9Sstevel@tonic-gate 			break;
2259*7c478bd9Sstevel@tonic-gate 		default:
2260*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "telnetd: illegal cmd line option %c",
2261*7c478bd9Sstevel@tonic-gate 			    c);
2262*7c478bd9Sstevel@tonic-gate 			break;
2263*7c478bd9Sstevel@tonic-gate 		}
2264*7c478bd9Sstevel@tonic-gate 	}
2265*7c478bd9Sstevel@tonic-gate 
2266*7c478bd9Sstevel@tonic-gate 	netibufsize = BUFSIZ;
2267*7c478bd9Sstevel@tonic-gate 	if (!(netibuf = (char *)malloc(netibufsize)))
2268*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "netibuf malloc failed\n");
2269*7c478bd9Sstevel@tonic-gate 	(void) memset(netibuf, 0, netibufsize);
2270*7c478bd9Sstevel@tonic-gate 	netip = netibuf;
2271*7c478bd9Sstevel@tonic-gate 
2272*7c478bd9Sstevel@tonic-gate #if	defined(DEBUG)
2273*7c478bd9Sstevel@tonic-gate 	if (standalone) {
2274*7c478bd9Sstevel@tonic-gate 		int s, ns, foo;
2275*7c478bd9Sstevel@tonic-gate 		struct servent *sp;
2276*7c478bd9Sstevel@tonic-gate 		static struct sockaddr_in6 sin6 = { AF_INET6 };
2277*7c478bd9Sstevel@tonic-gate 		int option = 1;
2278*7c478bd9Sstevel@tonic-gate 
2279*7c478bd9Sstevel@tonic-gate 		if (porttouse) {
2280*7c478bd9Sstevel@tonic-gate 			sin6.sin6_port = htons(porttouse);
2281*7c478bd9Sstevel@tonic-gate 		} else {
2282*7c478bd9Sstevel@tonic-gate 			sp = getservbyname("telnet", "tcp");
2283*7c478bd9Sstevel@tonic-gate 			if (sp == 0) {
2284*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2285*7c478bd9Sstevel@tonic-gate 					    "telnetd: tcp/telnet: "
2286*7c478bd9Sstevel@tonic-gate 					    "unknown service\n");
2287*7c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2288*7c478bd9Sstevel@tonic-gate 			}
2289*7c478bd9Sstevel@tonic-gate 			sin6.sin6_port = sp->s_port;
2290*7c478bd9Sstevel@tonic-gate 		}
2291*7c478bd9Sstevel@tonic-gate 
2292*7c478bd9Sstevel@tonic-gate 		s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
2293*7c478bd9Sstevel@tonic-gate 		if (s < 0) {
2294*7c478bd9Sstevel@tonic-gate 			perror("telnetd: socket");
2295*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2296*7c478bd9Sstevel@tonic-gate 		}
2297*7c478bd9Sstevel@tonic-gate 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
2298*7c478bd9Sstevel@tonic-gate 		    sizeof (option)) == -1)
2299*7c478bd9Sstevel@tonic-gate 			perror("setsockopt SO_REUSEADDR");
2300*7c478bd9Sstevel@tonic-gate 		if (bind(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
2301*7c478bd9Sstevel@tonic-gate 			perror("bind");
2302*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2303*7c478bd9Sstevel@tonic-gate 		}
2304*7c478bd9Sstevel@tonic-gate 		if (listen(s, 32) < 0) {
2305*7c478bd9Sstevel@tonic-gate 			perror("listen");
2306*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2307*7c478bd9Sstevel@tonic-gate 		}
2308*7c478bd9Sstevel@tonic-gate 
2309*7c478bd9Sstevel@tonic-gate 		/* automatically reap all child processes */
2310*7c478bd9Sstevel@tonic-gate 		(void) signal(SIGCHLD, SIG_IGN);
2311*7c478bd9Sstevel@tonic-gate 
2312*7c478bd9Sstevel@tonic-gate 		for (;;) {
2313*7c478bd9Sstevel@tonic-gate 			pid_t pid;
2314*7c478bd9Sstevel@tonic-gate 
2315*7c478bd9Sstevel@tonic-gate 			foo = sizeof (sin6);
2316*7c478bd9Sstevel@tonic-gate 			ns = accept(s, (struct sockaddr *)&sin6, &foo);
2317*7c478bd9Sstevel@tonic-gate 			if (ns < 0) {
2318*7c478bd9Sstevel@tonic-gate 				perror("accept");
2319*7c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2320*7c478bd9Sstevel@tonic-gate 			}
2321*7c478bd9Sstevel@tonic-gate 			pid = fork();
2322*7c478bd9Sstevel@tonic-gate 			if (pid == -1) {
2323*7c478bd9Sstevel@tonic-gate 				perror("fork");
2324*7c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2325*7c478bd9Sstevel@tonic-gate 			}
2326*7c478bd9Sstevel@tonic-gate 			if (pid == 0) {
2327*7c478bd9Sstevel@tonic-gate 				(void) dup2(ns, 0);
2328*7c478bd9Sstevel@tonic-gate 				(void) close(s);
2329*7c478bd9Sstevel@tonic-gate 				(void) signal(SIGCHLD, SIG_DFL);
2330*7c478bd9Sstevel@tonic-gate 				break;
2331*7c478bd9Sstevel@tonic-gate 			}
2332*7c478bd9Sstevel@tonic-gate 			(void) close(ns);
2333*7c478bd9Sstevel@tonic-gate 		}
2334*7c478bd9Sstevel@tonic-gate 	}
2335*7c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
2336*7c478bd9Sstevel@tonic-gate 
2337*7c478bd9Sstevel@tonic-gate 	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
2338*7c478bd9Sstevel@tonic-gate 
2339*7c478bd9Sstevel@tonic-gate 	issocket = issock(0);
2340*7c478bd9Sstevel@tonic-gate 	if (!issocket)
2341*7c478bd9Sstevel@tonic-gate 		fatal(0, "stdin is not a socket file descriptor");
2342*7c478bd9Sstevel@tonic-gate 
2343*7c478bd9Sstevel@tonic-gate 	fromlen = (socklen_t)sizeof (from);
2344*7c478bd9Sstevel@tonic-gate 	(void) memset((char *)&from, 0, sizeof (from));
2345*7c478bd9Sstevel@tonic-gate 	if (getpeername(0, (struct sockaddr *)&from, &fromlen)
2346*7c478bd9Sstevel@tonic-gate 	    < 0) {
2347*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", argv[0]);
2348*7c478bd9Sstevel@tonic-gate 		perror("getpeername");
2349*7c478bd9Sstevel@tonic-gate 		_exit(EXIT_FAILURE);
2350*7c478bd9Sstevel@tonic-gate 	}
2351*7c478bd9Sstevel@tonic-gate 
2352*7c478bd9Sstevel@tonic-gate 	if (audit_telnet_settid(0)) {	/* set terminal ID */
2353*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", argv[0]);
2354*7c478bd9Sstevel@tonic-gate 		perror("audit");
2355*7c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
2356*7c478bd9Sstevel@tonic-gate 	}
2357*7c478bd9Sstevel@tonic-gate 
2358*7c478bd9Sstevel@tonic-gate 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on,
2359*7c478bd9Sstevel@tonic-gate 						sizeof (on)) < 0) {
2360*7c478bd9Sstevel@tonic-gate 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
2361*7c478bd9Sstevel@tonic-gate 	}
2362*7c478bd9Sstevel@tonic-gate 
2363*7c478bd9Sstevel@tonic-gate 	/*
2364*7c478bd9Sstevel@tonic-gate 	 * Set the TOS value
2365*7c478bd9Sstevel@tonic-gate 	 */
2366*7c478bd9Sstevel@tonic-gate 	if (tos != -1 &&
2367*7c478bd9Sstevel@tonic-gate 	    setsockopt(0, IPPROTO_IP, IP_TOS,
2368*7c478bd9Sstevel@tonic-gate 		    (char *)&tos, sizeof (tos)) < 0 &&
2369*7c478bd9Sstevel@tonic-gate 		errno != ENOPROTOOPT) {
2370*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m", tos);
2371*7c478bd9Sstevel@tonic-gate 	}
2372*7c478bd9Sstevel@tonic-gate 
2373*7c478bd9Sstevel@tonic-gate 	if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
2374*7c478bd9Sstevel@tonic-gate 	    sizeof (on)) < 0) {
2375*7c478bd9Sstevel@tonic-gate 		syslog(LOG_WARNING, "setsockopt (SO_OOBINLINE): %m");
2376*7c478bd9Sstevel@tonic-gate 	}
2377*7c478bd9Sstevel@tonic-gate 
2378*7c478bd9Sstevel@tonic-gate 	/* set the default PAM service name */
2379*7c478bd9Sstevel@tonic-gate 	(void) strcpy(pam_svc_name, "telnet");
2380*7c478bd9Sstevel@tonic-gate 
2381*7c478bd9Sstevel@tonic-gate 	doit(0, &from);
2382*7c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
2383*7c478bd9Sstevel@tonic-gate 	return (EXIT_SUCCESS);
2384*7c478bd9Sstevel@tonic-gate }
2385*7c478bd9Sstevel@tonic-gate 
2386*7c478bd9Sstevel@tonic-gate static char	*terminaltype = 0;
2387*7c478bd9Sstevel@tonic-gate 
2388*7c478bd9Sstevel@tonic-gate /*
2389*7c478bd9Sstevel@tonic-gate  * ttloop
2390*7c478bd9Sstevel@tonic-gate  *
2391*7c478bd9Sstevel@tonic-gate  *	A small subroutine to flush the network output buffer, get some data
2392*7c478bd9Sstevel@tonic-gate  * from the network, and pass it through the telnet state machine.  We
2393*7c478bd9Sstevel@tonic-gate  * also flush the pty input buffer (by dropping its data) if it becomes
2394*7c478bd9Sstevel@tonic-gate  * too full.
2395*7c478bd9Sstevel@tonic-gate  */
2396*7c478bd9Sstevel@tonic-gate static void
2397*7c478bd9Sstevel@tonic-gate ttloop(void)
2398*7c478bd9Sstevel@tonic-gate {
2399*7c478bd9Sstevel@tonic-gate 	if (nfrontp-nbackp) {
2400*7c478bd9Sstevel@tonic-gate 		netflush();
2401*7c478bd9Sstevel@tonic-gate 	}
2402*7c478bd9Sstevel@tonic-gate read_again:
2403*7c478bd9Sstevel@tonic-gate 	ncc = read(net, netibuf, netibufsize);
2404*7c478bd9Sstevel@tonic-gate 	if (ncc < 0) {
2405*7c478bd9Sstevel@tonic-gate 		if (errno == EINTR)
2406*7c478bd9Sstevel@tonic-gate 			goto read_again;
2407*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ttloop:  read: %m");
2408*7c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
2409*7c478bd9Sstevel@tonic-gate 	} else if (ncc == 0) {
2410*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ttloop:  peer closed connection\n");
2411*7c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
2412*7c478bd9Sstevel@tonic-gate 	}
2413*7c478bd9Sstevel@tonic-gate 
2414*7c478bd9Sstevel@tonic-gate 	netip = netibuf;
2415*7c478bd9Sstevel@tonic-gate 	telrcv();		/* state machine */
2416*7c478bd9Sstevel@tonic-gate 	if (ncc > 0) {
2417*7c478bd9Sstevel@tonic-gate 		pfrontp = pbackp = ptyobuf;
2418*7c478bd9Sstevel@tonic-gate 		telrcv();
2419*7c478bd9Sstevel@tonic-gate 	}
2420*7c478bd9Sstevel@tonic-gate }
2421*7c478bd9Sstevel@tonic-gate 
2422*7c478bd9Sstevel@tonic-gate static void
2423*7c478bd9Sstevel@tonic-gate send_do(int option)
2424*7c478bd9Sstevel@tonic-gate {
2425*7c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)DO, (uchar_t)option);
2426*7c478bd9Sstevel@tonic-gate }
2427*7c478bd9Sstevel@tonic-gate 
2428*7c478bd9Sstevel@tonic-gate static void
2429*7c478bd9Sstevel@tonic-gate send_will(int option)
2430*7c478bd9Sstevel@tonic-gate {
2431*7c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WILL, (uchar_t)option);
2432*7c478bd9Sstevel@tonic-gate }
2433*7c478bd9Sstevel@tonic-gate 
2434*7c478bd9Sstevel@tonic-gate static void
2435*7c478bd9Sstevel@tonic-gate send_wont(int option)
2436*7c478bd9Sstevel@tonic-gate {
2437*7c478bd9Sstevel@tonic-gate 	write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WONT, (uchar_t)option);
2438*7c478bd9Sstevel@tonic-gate }
2439*7c478bd9Sstevel@tonic-gate 
2440*7c478bd9Sstevel@tonic-gate 
2441*7c478bd9Sstevel@tonic-gate /*
2442*7c478bd9Sstevel@tonic-gate  * getauthtype
2443*7c478bd9Sstevel@tonic-gate  *
2444*7c478bd9Sstevel@tonic-gate  * Negotiate automatic authentication, is possible.
2445*7c478bd9Sstevel@tonic-gate  */
2446*7c478bd9Sstevel@tonic-gate static int
2447*7c478bd9Sstevel@tonic-gate getauthtype(char *username, int *len)
2448*7c478bd9Sstevel@tonic-gate {
2449*7c478bd9Sstevel@tonic-gate 	int init_status = -1;
2450*7c478bd9Sstevel@tonic-gate 
2451*7c478bd9Sstevel@tonic-gate 	init_status = krb5_init();
2452*7c478bd9Sstevel@tonic-gate 
2453*7c478bd9Sstevel@tonic-gate 	if (auth_level == -1 || init_status != 0) {
2454*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_AUTHENTICATION] = OPT_NO;
2455*7c478bd9Sstevel@tonic-gate 		myopts[TELOPT_AUTHENTICATION] = OPT_NO;
2456*7c478bd9Sstevel@tonic-gate 		negotiate_auth_krb5 = B_FALSE;
2457*7c478bd9Sstevel@tonic-gate 		negotiate_encrypt = B_FALSE;
2458*7c478bd9Sstevel@tonic-gate 		return (AUTH_REJECT);
2459*7c478bd9Sstevel@tonic-gate 	}
2460*7c478bd9Sstevel@tonic-gate 
2461*7c478bd9Sstevel@tonic-gate 	if (init_status == 0 && auth_level != -1) {
2462*7c478bd9Sstevel@tonic-gate 		if (negotiate_auth_krb5) {
2463*7c478bd9Sstevel@tonic-gate 			/*
2464*7c478bd9Sstevel@tonic-gate 			 * Negotiate Authentication FIRST
2465*7c478bd9Sstevel@tonic-gate 			 */
2466*7c478bd9Sstevel@tonic-gate 			send_do(TELOPT_AUTHENTICATION);
2467*7c478bd9Sstevel@tonic-gate 			remopts[TELOPT_AUTHENTICATION] =
2468*7c478bd9Sstevel@tonic-gate 				OPT_YES_BUT_ALWAYS_LOOK;
2469*7c478bd9Sstevel@tonic-gate 		}
2470*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(authopt, getauth))
2471*7c478bd9Sstevel@tonic-gate 			ttloop();
2472*7c478bd9Sstevel@tonic-gate 
2473*7c478bd9Sstevel@tonic-gate 		if (remopts[TELOPT_AUTHENTICATION] == OPT_YES) {
2474*7c478bd9Sstevel@tonic-gate 			/*
2475*7c478bd9Sstevel@tonic-gate 			 * Request KRB5 Mutual authentication and if that fails,
2476*7c478bd9Sstevel@tonic-gate 			 * KRB5 1-way client authentication
2477*7c478bd9Sstevel@tonic-gate 			 */
2478*7c478bd9Sstevel@tonic-gate 			uchar_t sbbuf[MAXOPTLEN], *p;
2479*7c478bd9Sstevel@tonic-gate 			p = sbbuf;
2480*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)IAC;
2481*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)SB;
2482*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)TELOPT_AUTHENTICATION;
2483*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)TELQUAL_SEND;
2484*7c478bd9Sstevel@tonic-gate 			if (negotiate_auth_krb5) {
2485*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
2486*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT |
2487*7c478bd9Sstevel@tonic-gate 						AUTH_HOW_MUTUAL |
2488*7c478bd9Sstevel@tonic-gate 						AUTH_ENCRYPT_ON);
2489*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
2490*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT |
2491*7c478bd9Sstevel@tonic-gate 						AUTH_HOW_MUTUAL);
2492*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
2493*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)(AUTH_WHO_CLIENT|
2494*7c478bd9Sstevel@tonic-gate 						AUTH_HOW_ONE_WAY);
2495*7c478bd9Sstevel@tonic-gate 			} else {
2496*7c478bd9Sstevel@tonic-gate 				*p++ = (uchar_t)AUTHTYPE_NULL;
2497*7c478bd9Sstevel@tonic-gate 			}
2498*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)IAC;
2499*7c478bd9Sstevel@tonic-gate 			*p++ = (uchar_t)SE;
2500*7c478bd9Sstevel@tonic-gate 
2501*7c478bd9Sstevel@tonic-gate 			write_data_len((const char *)sbbuf,
2502*7c478bd9Sstevel@tonic-gate 				    (size_t)(p - sbbuf));
2503*7c478bd9Sstevel@tonic-gate 			netflush();
2504*7c478bd9Sstevel@tonic-gate 			if (auth_debug)
2505*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
2506*7c478bd9Sstevel@tonic-gate 					    "SENT TELOPT_AUTHENTICATION "
2507*7c478bd9Sstevel@tonic-gate 					    "[data]\n");
2508*7c478bd9Sstevel@tonic-gate 
2509*7c478bd9Sstevel@tonic-gate 			/* auth_wait returns the authentication level */
2510*7c478bd9Sstevel@tonic-gate 			/* status = auth_wait(username, len); */
2511*7c478bd9Sstevel@tonic-gate 			while (sequenceIs(authdone, getauth))
2512*7c478bd9Sstevel@tonic-gate 				ttloop();
2513*7c478bd9Sstevel@tonic-gate 			/*
2514*7c478bd9Sstevel@tonic-gate 			 * Now check to see if the user is valid or not
2515*7c478bd9Sstevel@tonic-gate 			 */
2516*7c478bd9Sstevel@tonic-gate 			if (authenticated == NULL || authenticated == &NoAuth)
2517*7c478bd9Sstevel@tonic-gate 				auth_status = AUTH_REJECT;
2518*7c478bd9Sstevel@tonic-gate 			else {
2519*7c478bd9Sstevel@tonic-gate 				/*
2520*7c478bd9Sstevel@tonic-gate 				 * We cant be VALID until the user status is
2521*7c478bd9Sstevel@tonic-gate 				 * checked.
2522*7c478bd9Sstevel@tonic-gate 				 */
2523*7c478bd9Sstevel@tonic-gate 				if (auth_status == AUTH_VALID)
2524*7c478bd9Sstevel@tonic-gate 					auth_status = AUTH_USER;
2525*7c478bd9Sstevel@tonic-gate 
2526*7c478bd9Sstevel@tonic-gate 				if (authenticated->AuthName ==
2527*7c478bd9Sstevel@tonic-gate 					AUTHTYPE_KERBEROS_V5)
2528*7c478bd9Sstevel@tonic-gate 					auth_status = krb5_user_status(
2529*7c478bd9Sstevel@tonic-gate 						username, *len, auth_status);
2530*7c478bd9Sstevel@tonic-gate 			}
2531*7c478bd9Sstevel@tonic-gate 		}
2532*7c478bd9Sstevel@tonic-gate 	}
2533*7c478bd9Sstevel@tonic-gate 	return (auth_status);
2534*7c478bd9Sstevel@tonic-gate }
2535*7c478bd9Sstevel@tonic-gate 
2536*7c478bd9Sstevel@tonic-gate static void
2537*7c478bd9Sstevel@tonic-gate getencrtype(void)
2538*7c478bd9Sstevel@tonic-gate {
2539*7c478bd9Sstevel@tonic-gate 	if (krb5_privacy_allowed() && negotiate_encrypt) {
2540*7c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ENCRYPT] != OPT_YES) {
2541*7c478bd9Sstevel@tonic-gate 			if (!sent_will_encrypt) {
2542*7c478bd9Sstevel@tonic-gate 				send_will(TELOPT_ENCRYPT);
2543*7c478bd9Sstevel@tonic-gate 				sent_will_encrypt = B_TRUE;
2544*7c478bd9Sstevel@tonic-gate 			}
2545*7c478bd9Sstevel@tonic-gate 			if (enc_debug)
2546*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "SENT WILL ENCRYPT\n");
2547*7c478bd9Sstevel@tonic-gate 		}
2548*7c478bd9Sstevel@tonic-gate 		if (remopts[TELOPT_ENCRYPT] != OPT_YES) {
2549*7c478bd9Sstevel@tonic-gate 			if (!sent_do_encrypt) {
2550*7c478bd9Sstevel@tonic-gate 				send_do(TELOPT_ENCRYPT);
2551*7c478bd9Sstevel@tonic-gate 				sent_do_encrypt = B_TRUE;
2552*7c478bd9Sstevel@tonic-gate 			}
2553*7c478bd9Sstevel@tonic-gate 			if (enc_debug)
2554*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, "SENT DO ENCRYPT\n");
2555*7c478bd9Sstevel@tonic-gate 		}
2556*7c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = OPT_YES;
2557*7c478bd9Sstevel@tonic-gate 
2558*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(encropt, getencr))
2559*7c478bd9Sstevel@tonic-gate 		    ttloop();
2560*7c478bd9Sstevel@tonic-gate 
2561*7c478bd9Sstevel@tonic-gate 		if (auth_status != AUTH_REJECT &&
2562*7c478bd9Sstevel@tonic-gate 		    remopts[TELOPT_ENCRYPT] == OPT_YES &&
2563*7c478bd9Sstevel@tonic-gate 		    myopts[TELOPT_ENCRYPT] == OPT_YES) {
2564*7c478bd9Sstevel@tonic-gate 
2565*7c478bd9Sstevel@tonic-gate 			if (sent_encrypt_support == B_FALSE) {
2566*7c478bd9Sstevel@tonic-gate 				write_data("%c%c%c%c%c%c%c",
2567*7c478bd9Sstevel@tonic-gate 					(uchar_t)IAC,
2568*7c478bd9Sstevel@tonic-gate 					(uchar_t)SB,
2569*7c478bd9Sstevel@tonic-gate 					(uchar_t)TELOPT_ENCRYPT,
2570*7c478bd9Sstevel@tonic-gate 					(uchar_t)ENCRYPT_SUPPORT,
2571*7c478bd9Sstevel@tonic-gate 					(uchar_t)TELOPT_ENCTYPE_DES_CFB64,
2572*7c478bd9Sstevel@tonic-gate 					(uchar_t)IAC,
2573*7c478bd9Sstevel@tonic-gate 					(uchar_t)SE);
2574*7c478bd9Sstevel@tonic-gate 
2575*7c478bd9Sstevel@tonic-gate 				netflush();
2576*7c478bd9Sstevel@tonic-gate 			}
2577*7c478bd9Sstevel@tonic-gate 			/*
2578*7c478bd9Sstevel@tonic-gate 			 * Now wait for a response to these messages before
2579*7c478bd9Sstevel@tonic-gate 			 * continuing...
2580*7c478bd9Sstevel@tonic-gate 			 * Look for TELOPT_ENCRYPT suboptions
2581*7c478bd9Sstevel@tonic-gate 			 */
2582*7c478bd9Sstevel@tonic-gate 			while (sequenceIs(encr_support, getencr))
2583*7c478bd9Sstevel@tonic-gate 				ttloop();
2584*7c478bd9Sstevel@tonic-gate 		}
2585*7c478bd9Sstevel@tonic-gate 	} else {
2586*7c478bd9Sstevel@tonic-gate 		/* Dont need responses to these, so dont wait for them */
2587*7c478bd9Sstevel@tonic-gate 		settimer(encropt);
2588*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] = OPT_NO;
2589*7c478bd9Sstevel@tonic-gate 		myopts[TELOPT_ENCRYPT] = OPT_NO;
2590*7c478bd9Sstevel@tonic-gate 	}
2591*7c478bd9Sstevel@tonic-gate 
2592*7c478bd9Sstevel@tonic-gate }
2593*7c478bd9Sstevel@tonic-gate 
2594*7c478bd9Sstevel@tonic-gate /*
2595*7c478bd9Sstevel@tonic-gate  * getterminaltype
2596*7c478bd9Sstevel@tonic-gate  *
2597*7c478bd9Sstevel@tonic-gate  * Ask the other end to send along its terminal type.
2598*7c478bd9Sstevel@tonic-gate  * Output is the variable terminaltype filled in.
2599*7c478bd9Sstevel@tonic-gate  */
2600*7c478bd9Sstevel@tonic-gate static void
2601*7c478bd9Sstevel@tonic-gate getterminaltype(void)
2602*7c478bd9Sstevel@tonic-gate {
2603*7c478bd9Sstevel@tonic-gate 	/*
2604*7c478bd9Sstevel@tonic-gate 	 * The remote side may have already sent this info, so
2605*7c478bd9Sstevel@tonic-gate 	 * dont ask for these options if the other side already
2606*7c478bd9Sstevel@tonic-gate 	 * sent the information.
2607*7c478bd9Sstevel@tonic-gate 	 */
2608*7c478bd9Sstevel@tonic-gate 	if (sequenceIs(ttypeopt, getterminal)) {
2609*7c478bd9Sstevel@tonic-gate 		send_do(TELOPT_TTYPE);
2610*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
2611*7c478bd9Sstevel@tonic-gate 	}
2612*7c478bd9Sstevel@tonic-gate 
2613*7c478bd9Sstevel@tonic-gate 	if (sequenceIs(nawsopt, getterminal)) {
2614*7c478bd9Sstevel@tonic-gate 		send_do(TELOPT_NAWS);
2615*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_NAWS] = OPT_YES_BUT_ALWAYS_LOOK;
2616*7c478bd9Sstevel@tonic-gate 	}
2617*7c478bd9Sstevel@tonic-gate 
2618*7c478bd9Sstevel@tonic-gate 	if (sequenceIs(xdisplocopt, getterminal)) {
2619*7c478bd9Sstevel@tonic-gate 		send_do(TELOPT_XDISPLOC);
2620*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_XDISPLOC] = OPT_YES_BUT_ALWAYS_LOOK;
2621*7c478bd9Sstevel@tonic-gate 	}
2622*7c478bd9Sstevel@tonic-gate 
2623*7c478bd9Sstevel@tonic-gate 	if (sequenceIs(environopt, getterminal)) {
2624*7c478bd9Sstevel@tonic-gate 		send_do(TELOPT_NEW_ENVIRON);
2625*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_NEW_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
2626*7c478bd9Sstevel@tonic-gate 	}
2627*7c478bd9Sstevel@tonic-gate 
2628*7c478bd9Sstevel@tonic-gate 	if (sequenceIs(oenvironopt, getterminal)) {
2629*7c478bd9Sstevel@tonic-gate 		send_do(TELOPT_OLD_ENVIRON);
2630*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_OLD_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
2631*7c478bd9Sstevel@tonic-gate 	}
2632*7c478bd9Sstevel@tonic-gate 
2633*7c478bd9Sstevel@tonic-gate 	/* make sure encryption is started here */
2634*7c478bd9Sstevel@tonic-gate 	while (auth_status != AUTH_REJECT &&
2635*7c478bd9Sstevel@tonic-gate 		authenticated != &NoAuth && authenticated != NULL &&
2636*7c478bd9Sstevel@tonic-gate 		remopts[TELOPT_ENCRYPT] == OPT_YES &&
2637*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.autoflag &&
2638*7c478bd9Sstevel@tonic-gate 		encr_data.encrypt.state != ENCR_STATE_OK) {
2639*7c478bd9Sstevel@tonic-gate 	    if (enc_debug)
2640*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "getterminaltype() forcing encrypt\n");
2641*7c478bd9Sstevel@tonic-gate 	    ttloop();
2642*7c478bd9Sstevel@tonic-gate 	}
2643*7c478bd9Sstevel@tonic-gate 
2644*7c478bd9Sstevel@tonic-gate 	if (enc_debug) {
2645*7c478bd9Sstevel@tonic-gate 	    (void) fprintf(stderr, "getterminaltype() encryption %sstarted\n",
2646*7c478bd9Sstevel@tonic-gate 		    encr_data.encrypt.state == ENCR_STATE_OK ? "" : "not ");
2647*7c478bd9Sstevel@tonic-gate 	}
2648*7c478bd9Sstevel@tonic-gate 
2649*7c478bd9Sstevel@tonic-gate 	while (sequenceIs(ttypeopt, getterminal) ||
2650*7c478bd9Sstevel@tonic-gate 	    sequenceIs(nawsopt, getterminal) ||
2651*7c478bd9Sstevel@tonic-gate 	    sequenceIs(xdisplocopt, getterminal) ||
2652*7c478bd9Sstevel@tonic-gate 	    sequenceIs(environopt, getterminal) ||
2653*7c478bd9Sstevel@tonic-gate 	    sequenceIs(oenvironopt, getterminal)) {
2654*7c478bd9Sstevel@tonic-gate 		ttloop();
2655*7c478bd9Sstevel@tonic-gate 	}
2656*7c478bd9Sstevel@tonic-gate 
2657*7c478bd9Sstevel@tonic-gate 
2658*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_TTYPE] == OPT_YES) {
2659*7c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
2660*7c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_TTYPE, (uchar_t)TELQUAL_SEND,
2661*7c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
2662*7c478bd9Sstevel@tonic-gate 
2663*7c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
2664*7c478bd9Sstevel@tonic-gate 	}
2665*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
2666*7c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
2667*7c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_XDISPLOC, (uchar_t)TELQUAL_SEND,
2668*7c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
2669*7c478bd9Sstevel@tonic-gate 
2670*7c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
2671*7c478bd9Sstevel@tonic-gate 	}
2672*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
2673*7c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
2674*7c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_NEW_ENVIRON, (uchar_t)TELQUAL_SEND,
2675*7c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
2676*7c478bd9Sstevel@tonic-gate 
2677*7c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
2678*7c478bd9Sstevel@tonic-gate 	}
2679*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
2680*7c478bd9Sstevel@tonic-gate 		static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
2681*7c478bd9Sstevel@tonic-gate 		    (uchar_t)TELOPT_OLD_ENVIRON, (uchar_t)TELQUAL_SEND,
2682*7c478bd9Sstevel@tonic-gate 		    (uchar_t)IAC, (uchar_t)SE };
2683*7c478bd9Sstevel@tonic-gate 
2684*7c478bd9Sstevel@tonic-gate 		write_data_len((const char *)sbbuf, sizeof (sbbuf));
2685*7c478bd9Sstevel@tonic-gate 	}
2686*7c478bd9Sstevel@tonic-gate 
2687*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_TTYPE] == OPT_YES) {
2688*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(ttypesubopt, getterminal)) {
2689*7c478bd9Sstevel@tonic-gate 			ttloop();
2690*7c478bd9Sstevel@tonic-gate 		}
2691*7c478bd9Sstevel@tonic-gate 	}
2692*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
2693*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(xdisplocsubopt, getterminal)) {
2694*7c478bd9Sstevel@tonic-gate 			ttloop();
2695*7c478bd9Sstevel@tonic-gate 		}
2696*7c478bd9Sstevel@tonic-gate 	}
2697*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
2698*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(environsubopt, getterminal)) {
2699*7c478bd9Sstevel@tonic-gate 			ttloop();
2700*7c478bd9Sstevel@tonic-gate 		}
2701*7c478bd9Sstevel@tonic-gate 	}
2702*7c478bd9Sstevel@tonic-gate 	if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
2703*7c478bd9Sstevel@tonic-gate 		while (sequenceIs(oenvironsubopt, getterminal)) {
2704*7c478bd9Sstevel@tonic-gate 			ttloop();
2705*7c478bd9Sstevel@tonic-gate 		}
2706*7c478bd9Sstevel@tonic-gate 	}
2707*7c478bd9Sstevel@tonic-gate 	init_neg_done = 1;
2708*7c478bd9Sstevel@tonic-gate }
2709*7c478bd9Sstevel@tonic-gate 
2710*7c478bd9Sstevel@tonic-gate pid_t pid;
2711*7c478bd9Sstevel@tonic-gate 
2712*7c478bd9Sstevel@tonic-gate /*
2713*7c478bd9Sstevel@tonic-gate  * Get a pty, scan input lines.
2714*7c478bd9Sstevel@tonic-gate  */
2715*7c478bd9Sstevel@tonic-gate static void
2716*7c478bd9Sstevel@tonic-gate doit(int f, struct sockaddr_storage *who)
2717*7c478bd9Sstevel@tonic-gate {
2718*7c478bd9Sstevel@tonic-gate 	char *host;
2719*7c478bd9Sstevel@tonic-gate 	char host_name[MAXHOSTNAMELEN];
2720*7c478bd9Sstevel@tonic-gate 	int p, t, tt;
2721*7c478bd9Sstevel@tonic-gate 	struct sgttyb b;
2722*7c478bd9Sstevel@tonic-gate 	int	ptmfd;	/* fd of logindmux connected to pty */
2723*7c478bd9Sstevel@tonic-gate 	int	netfd;	/* fd of logindmux connected to netf */
2724*7c478bd9Sstevel@tonic-gate 	struct	stat	buf;
2725*7c478bd9Sstevel@tonic-gate 	struct	protocol_arg	telnetp;
2726*7c478bd9Sstevel@tonic-gate 	struct	strioctl	telnetmod;
2727*7c478bd9Sstevel@tonic-gate 	struct	envlist	*env, *next;
2728*7c478bd9Sstevel@tonic-gate 	int	nsize = 0;
2729*7c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
2730*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
2731*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
2732*7c478bd9Sstevel@tonic-gate 	socklen_t wholen;
2733*7c478bd9Sstevel@tonic-gate 	char username[MAXUSERNAMELEN];
2734*7c478bd9Sstevel@tonic-gate 	int len;
2735*7c478bd9Sstevel@tonic-gate 	uchar_t passthru;
2736*7c478bd9Sstevel@tonic-gate 	char *slavename;
2737*7c478bd9Sstevel@tonic-gate 
2738*7c478bd9Sstevel@tonic-gate 	if ((p = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) {
2739*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/ptmx", errno);
2740*7c478bd9Sstevel@tonic-gate 	}
2741*7c478bd9Sstevel@tonic-gate 	if (grantpt(p) == -1)
2742*7c478bd9Sstevel@tonic-gate 		fatal(f, "could not grant slave pty");
2743*7c478bd9Sstevel@tonic-gate 	if (unlockpt(p) == -1)
2744*7c478bd9Sstevel@tonic-gate 		fatal(f, "could not unlock slave pty");
2745*7c478bd9Sstevel@tonic-gate 	if ((slavename = ptsname(p)) == NULL)
2746*7c478bd9Sstevel@tonic-gate 		fatal(f, "could not enable slave pty");
2747*7c478bd9Sstevel@tonic-gate 	(void) dup2(f, 0);
2748*7c478bd9Sstevel@tonic-gate 	if ((t = open(slavename, O_RDWR | O_NOCTTY)) == -1)
2749*7c478bd9Sstevel@tonic-gate 		fatal(f, "could not open slave pty");
2750*7c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ptem") == -1)
2751*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ptem", errno);
2752*7c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ldterm") == -1)
2753*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ldterm", errno);
2754*7c478bd9Sstevel@tonic-gate 	if (ioctl(t, I_PUSH, "ttcompat") == -1)
2755*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH ttcompat", errno);
2756*7c478bd9Sstevel@tonic-gate 
2757*7c478bd9Sstevel@tonic-gate 	line = slavename;
2758*7c478bd9Sstevel@tonic-gate 
2759*7c478bd9Sstevel@tonic-gate 	pty = t;
2760*7c478bd9Sstevel@tonic-gate 
2761*7c478bd9Sstevel@tonic-gate 	if (ioctl(t, TIOCGETP, &b) == -1)
2762*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP pty t: %m\n");
2763*7c478bd9Sstevel@tonic-gate 	b.sg_flags = O_CRMOD|O_XTABS|O_ANYP;
2764*7c478bd9Sstevel@tonic-gate 	/* XXX - ispeed and ospeed must be non-zero */
2765*7c478bd9Sstevel@tonic-gate 	b.sg_ispeed = B38400;
2766*7c478bd9Sstevel@tonic-gate 	b.sg_ospeed = B38400;
2767*7c478bd9Sstevel@tonic-gate 	if (ioctl(t, TIOCSETN, &b) == -1)
2768*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCSETN pty t: %m\n");
2769*7c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCGETP, &b) == -1)
2770*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP pty pty: %m\n");
2771*7c478bd9Sstevel@tonic-gate 	b.sg_flags &= ~O_ECHO;
2772*7c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCSETN, &b) == -1)
2773*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCSETN pty pty: %m\n");
2774*7c478bd9Sstevel@tonic-gate 
2775*7c478bd9Sstevel@tonic-gate 	if (who->ss_family == AF_INET) {
2776*7c478bd9Sstevel@tonic-gate 		char *addrbuf = NULL;
2777*7c478bd9Sstevel@tonic-gate 		char *portbuf = NULL;
2778*7c478bd9Sstevel@tonic-gate 
2779*7c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)who;
2780*7c478bd9Sstevel@tonic-gate 		wholen = sizeof (struct sockaddr_in);
2781*7c478bd9Sstevel@tonic-gate 
2782*7c478bd9Sstevel@tonic-gate 		addrbuf = (char *)malloc(wholen);
2783*7c478bd9Sstevel@tonic-gate 		if (addrbuf == NULL)
2784*7c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for address info\n");
2785*7c478bd9Sstevel@tonic-gate 		portbuf = (char *)malloc(sizeof (sin->sin_port));
2786*7c478bd9Sstevel@tonic-gate 		if (portbuf == NULL) {
2787*7c478bd9Sstevel@tonic-gate 			free(addrbuf);
2788*7c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for port info\n");
2789*7c478bd9Sstevel@tonic-gate 		}
2790*7c478bd9Sstevel@tonic-gate 
2791*7c478bd9Sstevel@tonic-gate 		(void) memcpy(addrbuf, (const void *)&sin->sin_addr, wholen);
2792*7c478bd9Sstevel@tonic-gate 		(void) memcpy(portbuf, (const void *)&sin->sin_port,
2793*7c478bd9Sstevel@tonic-gate 			    sizeof (sin->sin_port));
2794*7c478bd9Sstevel@tonic-gate 
2795*7c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
2796*7c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
2797*7c478bd9Sstevel@tonic-gate 
2798*7c478bd9Sstevel@tonic-gate 		rsaddr.contents = (krb5_octet *)addrbuf;
2799*7c478bd9Sstevel@tonic-gate 		rsaddr.length = wholen;
2800*7c478bd9Sstevel@tonic-gate 		rsaddr.addrtype = ADDRTYPE_INET;
2801*7c478bd9Sstevel@tonic-gate 
2802*7c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
2803*7c478bd9Sstevel@tonic-gate 			free(rsport.contents);
2804*7c478bd9Sstevel@tonic-gate 
2805*7c478bd9Sstevel@tonic-gate 		rsport.contents = (krb5_octet *)portbuf;
2806*7c478bd9Sstevel@tonic-gate 		rsport.length = sizeof (sin->sin_port);
2807*7c478bd9Sstevel@tonic-gate 		rsport.addrtype = ADDRTYPE_IPPORT;
2808*7c478bd9Sstevel@tonic-gate 	} else if (who->ss_family == AF_INET6) {
2809*7c478bd9Sstevel@tonic-gate 		struct in_addr ipv4_addr;
2810*7c478bd9Sstevel@tonic-gate 		char *addrbuf = NULL;
2811*7c478bd9Sstevel@tonic-gate 		char *portbuf = NULL;
2812*7c478bd9Sstevel@tonic-gate 
2813*7c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)who;
2814*7c478bd9Sstevel@tonic-gate 		wholen = sizeof (struct sockaddr_in6);
2815*7c478bd9Sstevel@tonic-gate 
2816*7c478bd9Sstevel@tonic-gate 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
2817*7c478bd9Sstevel@tonic-gate 				    &ipv4_addr);
2818*7c478bd9Sstevel@tonic-gate 
2819*7c478bd9Sstevel@tonic-gate 		addrbuf = (char *)malloc(wholen);
2820*7c478bd9Sstevel@tonic-gate 		if (addrbuf == NULL)
2821*7c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for address info\n");
2822*7c478bd9Sstevel@tonic-gate 
2823*7c478bd9Sstevel@tonic-gate 		portbuf = (char *)malloc(sizeof (sin6->sin6_port));
2824*7c478bd9Sstevel@tonic-gate 		if (portbuf == NULL) {
2825*7c478bd9Sstevel@tonic-gate 			free(addrbuf);
2826*7c478bd9Sstevel@tonic-gate 			fatal(f, "Cannot alloc memory for port info\n");
2827*7c478bd9Sstevel@tonic-gate 		}
2828*7c478bd9Sstevel@tonic-gate 
2829*7c478bd9Sstevel@tonic-gate 		(void) memcpy((void *) addrbuf,
2830*7c478bd9Sstevel@tonic-gate 			    (const void *)&ipv4_addr,
2831*7c478bd9Sstevel@tonic-gate 			    wholen);
2832*7c478bd9Sstevel@tonic-gate 		/*
2833*7c478bd9Sstevel@tonic-gate 		 * If we already used rsaddr.contents, free the previous
2834*7c478bd9Sstevel@tonic-gate 		 * buffer.
2835*7c478bd9Sstevel@tonic-gate 		 */
2836*7c478bd9Sstevel@tonic-gate 		if (rsaddr.contents != NULL)
2837*7c478bd9Sstevel@tonic-gate 			free(rsaddr.contents);
2838*7c478bd9Sstevel@tonic-gate 
2839*7c478bd9Sstevel@tonic-gate 		rsaddr.contents = (krb5_octet *)addrbuf;
2840*7c478bd9Sstevel@tonic-gate 		rsaddr.length = sizeof (ipv4_addr);
2841*7c478bd9Sstevel@tonic-gate 		rsaddr.addrtype = ADDRTYPE_INET;
2842*7c478bd9Sstevel@tonic-gate 
2843*7c478bd9Sstevel@tonic-gate 		(void) memcpy((void *) portbuf, (const void *)&sin6->sin6_port,
2844*7c478bd9Sstevel@tonic-gate 			    sizeof (sin6->sin6_port));
2845*7c478bd9Sstevel@tonic-gate 
2846*7c478bd9Sstevel@tonic-gate 		if (rsport.contents != NULL)
2847*7c478bd9Sstevel@tonic-gate 			free(rsport.contents);
2848*7c478bd9Sstevel@tonic-gate 
2849*7c478bd9Sstevel@tonic-gate 		rsport.contents = (krb5_octet *)portbuf;
2850*7c478bd9Sstevel@tonic-gate 		rsport.length = sizeof (sin6->sin6_port);
2851*7c478bd9Sstevel@tonic-gate 		rsport.addrtype = ADDRTYPE_IPPORT;
2852*7c478bd9Sstevel@tonic-gate 	} else {
2853*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "unknown address family %d\n",
2854*7c478bd9Sstevel@tonic-gate 		    who->ss_family);
2855*7c478bd9Sstevel@tonic-gate 		fatal(f, "getpeername: unknown address family\n");
2856*7c478bd9Sstevel@tonic-gate 	}
2857*7c478bd9Sstevel@tonic-gate 
2858*7c478bd9Sstevel@tonic-gate 	if (getnameinfo((const struct sockaddr *) who, wholen, host_name,
2859*7c478bd9Sstevel@tonic-gate 	    sizeof (host_name), NULL, 0, 0) == 0) {
2860*7c478bd9Sstevel@tonic-gate 		host = host_name;
2861*7c478bd9Sstevel@tonic-gate 	} else {
2862*7c478bd9Sstevel@tonic-gate 		/*
2863*7c478bd9Sstevel@tonic-gate 		 * If the '-U' option was given on the cmd line, we must
2864*7c478bd9Sstevel@tonic-gate 		 * be able to lookup the hostname
2865*7c478bd9Sstevel@tonic-gate 		 */
2866*7c478bd9Sstevel@tonic-gate 		if (resolve_hostname) {
2867*7c478bd9Sstevel@tonic-gate 			fatal(f, "Couldn't resolve your address into a "
2868*7c478bd9Sstevel@tonic-gate 			    "host name.\r\nPlease contact your net "
2869*7c478bd9Sstevel@tonic-gate 			    "administrator");
2870*7c478bd9Sstevel@tonic-gate 		}
2871*7c478bd9Sstevel@tonic-gate 
2872*7c478bd9Sstevel@tonic-gate 		if (who->ss_family == AF_INET6) {
2873*7c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
2874*7c478bd9Sstevel@tonic-gate 				struct in_addr ipv4_addr;
2875*7c478bd9Sstevel@tonic-gate 
2876*7c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
2877*7c478bd9Sstevel@tonic-gate 				    &ipv4_addr);
2878*7c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET,
2879*7c478bd9Sstevel@tonic-gate 				    &ipv4_addr, abuf, sizeof (abuf));
2880*7c478bd9Sstevel@tonic-gate 			} else {
2881*7c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET6,
2882*7c478bd9Sstevel@tonic-gate 				    &sin6->sin6_addr, abuf,
2883*7c478bd9Sstevel@tonic-gate 				    sizeof (abuf));
2884*7c478bd9Sstevel@tonic-gate 			}
2885*7c478bd9Sstevel@tonic-gate 		} else if (who->ss_family == AF_INET) {
2886*7c478bd9Sstevel@tonic-gate 				host = (char *)inet_ntop(AF_INET,
2887*7c478bd9Sstevel@tonic-gate 				    &sin->sin_addr, abuf, sizeof (abuf));
2888*7c478bd9Sstevel@tonic-gate 			}
2889*7c478bd9Sstevel@tonic-gate 	}
2890*7c478bd9Sstevel@tonic-gate 	/*
2891*7c478bd9Sstevel@tonic-gate 	 * Note that sockmod has to be removed since readstream assumes
2892*7c478bd9Sstevel@tonic-gate 	 * a "raw" TPI endpoint (e.g. it uses getmsg).
2893*7c478bd9Sstevel@tonic-gate 	 */
2894*7c478bd9Sstevel@tonic-gate 	if (removemod(f, "sockmod") < 0)
2895*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "couldn't remove sockmod", errno);
2896*7c478bd9Sstevel@tonic-gate 
2897*7c478bd9Sstevel@tonic-gate 	encrypt_init();
2898*7c478bd9Sstevel@tonic-gate 
2899*7c478bd9Sstevel@tonic-gate 	/*
2900*7c478bd9Sstevel@tonic-gate 	 * Push the crypto module on the stream before 'telmod' so it
2901*7c478bd9Sstevel@tonic-gate 	 * can encrypt/decrypt without interfering with telmod functionality
2902*7c478bd9Sstevel@tonic-gate 	 * We must push it now because many of the crypto options negotiated
2903*7c478bd9Sstevel@tonic-gate 	 * initially must be saved in the crypto module (via IOCTL calls).
2904*7c478bd9Sstevel@tonic-gate 	 */
2905*7c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_PUSH, "cryptmod") < 0)
2906*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH cryptmod", errno);
2907*7c478bd9Sstevel@tonic-gate 
2908*7c478bd9Sstevel@tonic-gate 	cryptmod_fd = f;
2909*7c478bd9Sstevel@tonic-gate 	/*
2910*7c478bd9Sstevel@tonic-gate 	 * gotta set the encryption clock now because it is often negotiated
2911*7c478bd9Sstevel@tonic-gate 	 * immediately by the client, and if we wait till after we negotiate
2912*7c478bd9Sstevel@tonic-gate 	 * auth, it will be out of whack with when the WILL/WONT ENCRYPT
2913*7c478bd9Sstevel@tonic-gate 	 * option is received.
2914*7c478bd9Sstevel@tonic-gate 	 */
2915*7c478bd9Sstevel@tonic-gate 	settimer(getencr);
2916*7c478bd9Sstevel@tonic-gate 
2917*7c478bd9Sstevel@tonic-gate 	/*
2918*7c478bd9Sstevel@tonic-gate 	 * get terminal type.
2919*7c478bd9Sstevel@tonic-gate 	 */
2920*7c478bd9Sstevel@tonic-gate 	username[0] = '\0';
2921*7c478bd9Sstevel@tonic-gate 	len = sizeof (username);
2922*7c478bd9Sstevel@tonic-gate 
2923*7c478bd9Sstevel@tonic-gate 	settimer(getterminal);
2924*7c478bd9Sstevel@tonic-gate 	settimer(getauth);
2925*7c478bd9Sstevel@tonic-gate 	/*
2926*7c478bd9Sstevel@tonic-gate 	 * Exchange TELOPT_AUTHENTICATE options per RFC 2941/2942
2927*7c478bd9Sstevel@tonic-gate 	 */
2928*7c478bd9Sstevel@tonic-gate 	auth_status = getauthtype(username, &len);
2929*7c478bd9Sstevel@tonic-gate 	/*
2930*7c478bd9Sstevel@tonic-gate 	 * Exchange TELOPT_ENCRYPT options per RFC 2946
2931*7c478bd9Sstevel@tonic-gate 	 */
2932*7c478bd9Sstevel@tonic-gate 	getencrtype();
2933*7c478bd9Sstevel@tonic-gate 	getterminaltype();
2934*7c478bd9Sstevel@tonic-gate 
2935*7c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_PUSH, "telmod") < 0)
2936*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "ioctl I_PUSH telmod", errno);
2937*7c478bd9Sstevel@tonic-gate 
2938*7c478bd9Sstevel@tonic-gate 	/*
2939*7c478bd9Sstevel@tonic-gate 	 * Make sure telmod will pass unrecognized IOCTLs to cryptmod
2940*7c478bd9Sstevel@tonic-gate 	 */
2941*7c478bd9Sstevel@tonic-gate 	passthru = 1;
2942*7c478bd9Sstevel@tonic-gate 
2943*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = CRYPTPASSTHRU;
2944*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
2945*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (uchar_t);
2946*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&passthru;
2947*7c478bd9Sstevel@tonic-gate 
2948*7c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_STR, &telnetmod) < 0)
2949*7c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl CRPASSTHRU failed\n");
2950*7c478bd9Sstevel@tonic-gate 
2951*7c478bd9Sstevel@tonic-gate 	if (!ncc)
2952*7c478bd9Sstevel@tonic-gate 		netip = netibuf;
2953*7c478bd9Sstevel@tonic-gate 
2954*7c478bd9Sstevel@tonic-gate 	/*
2955*7c478bd9Sstevel@tonic-gate 	 * readstream will do a getmsg till it receives M_PROTO type
2956*7c478bd9Sstevel@tonic-gate 	 * T_DATA_REQ from telnetmodopen().  This signals that all data
2957*7c478bd9Sstevel@tonic-gate 	 * in-flight before telmod was pushed has been received at the
2958*7c478bd9Sstevel@tonic-gate 	 * stream head.
2959*7c478bd9Sstevel@tonic-gate 	 */
2960*7c478bd9Sstevel@tonic-gate 	while ((nsize = readstream(f, netibuf, ncc + netip - netibuf)) > 0) {
2961*7c478bd9Sstevel@tonic-gate 		ncc += nsize;
2962*7c478bd9Sstevel@tonic-gate 	}
2963*7c478bd9Sstevel@tonic-gate 
2964*7c478bd9Sstevel@tonic-gate 	if (nsize < 0) {
2965*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "readstream failed\n", errno);
2966*7c478bd9Sstevel@tonic-gate 	}
2967*7c478bd9Sstevel@tonic-gate 
2968*7c478bd9Sstevel@tonic-gate 	/*
2969*7c478bd9Sstevel@tonic-gate 	 * open logindmux drivers and link them with network and ptm
2970*7c478bd9Sstevel@tonic-gate 	 * file descriptors.
2971*7c478bd9Sstevel@tonic-gate 	 */
2972*7c478bd9Sstevel@tonic-gate 	if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1) {
2973*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/logindmux", errno);
2974*7c478bd9Sstevel@tonic-gate 	}
2975*7c478bd9Sstevel@tonic-gate 	if ((netfd = open("/dev/logindmux", O_RDWR)) == -1) {
2976*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "open /dev/logindmux", errno);
2977*7c478bd9Sstevel@tonic-gate 	}
2978*7c478bd9Sstevel@tonic-gate 
2979*7c478bd9Sstevel@tonic-gate 	if (ioctl(ptmfd, I_LINK, p) < 0)
2980*7c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
2981*7c478bd9Sstevel@tonic-gate 	if (ioctl(netfd, I_LINK, f) < 0)
2982*7c478bd9Sstevel@tonic-gate 		fatal(f, "ioctl I_LINK of tcp connection failed\n");
2983*7c478bd9Sstevel@tonic-gate 
2984*7c478bd9Sstevel@tonic-gate 	/*
2985*7c478bd9Sstevel@tonic-gate 	 * Figure out the device number of ptm's mux fd, and pass that
2986*7c478bd9Sstevel@tonic-gate 	 * to the net's mux.
2987*7c478bd9Sstevel@tonic-gate 	 */
2988*7c478bd9Sstevel@tonic-gate 	if (fstat(ptmfd, &buf) < 0) {
2989*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "fstat ptmfd failed", errno);
2990*7c478bd9Sstevel@tonic-gate 	}
2991*7c478bd9Sstevel@tonic-gate 	telnetp.dev = buf.st_rdev;
2992*7c478bd9Sstevel@tonic-gate 	telnetp.flag = 0;
2993*7c478bd9Sstevel@tonic-gate 
2994*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
2995*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
2996*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (struct protocol_arg);
2997*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&telnetp;
2998*7c478bd9Sstevel@tonic-gate 
2999*7c478bd9Sstevel@tonic-gate 	if (ioctl(netfd, I_STR, &telnetmod) < 0)
3000*7c478bd9Sstevel@tonic-gate 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
3001*7c478bd9Sstevel@tonic-gate 
3002*7c478bd9Sstevel@tonic-gate 	/*
3003*7c478bd9Sstevel@tonic-gate 	 * Figure out the device number of the net's mux fd, and pass that
3004*7c478bd9Sstevel@tonic-gate 	 * to the ptm's mux.
3005*7c478bd9Sstevel@tonic-gate 	 */
3006*7c478bd9Sstevel@tonic-gate 	if (fstat(netfd, &buf) < 0) {
3007*7c478bd9Sstevel@tonic-gate 		fatalperror(f, "fstat netfd failed", errno);
3008*7c478bd9Sstevel@tonic-gate 	}
3009*7c478bd9Sstevel@tonic-gate 	telnetp.dev = buf.st_rdev;
3010*7c478bd9Sstevel@tonic-gate 	telnetp.flag = 1;
3011*7c478bd9Sstevel@tonic-gate 
3012*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
3013*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_timout = -1;
3014*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_len = sizeof (struct protocol_arg);
3015*7c478bd9Sstevel@tonic-gate 	telnetmod.ic_dp = (char *)&telnetp;
3016*7c478bd9Sstevel@tonic-gate 
3017*7c478bd9Sstevel@tonic-gate 	if (ioctl(ptmfd, I_STR, &telnetmod) < 0)
3018*7c478bd9Sstevel@tonic-gate 		fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of ptmfd failed\n");
3019*7c478bd9Sstevel@tonic-gate 
3020*7c478bd9Sstevel@tonic-gate 	net = netfd;
3021*7c478bd9Sstevel@tonic-gate 	master = ptmfd;
3022*7c478bd9Sstevel@tonic-gate 	cryptmod_fd = netfd;
3023*7c478bd9Sstevel@tonic-gate 
3024*7c478bd9Sstevel@tonic-gate 	/*
3025*7c478bd9Sstevel@tonic-gate 	 * Show banner that getty never gave, but
3026*7c478bd9Sstevel@tonic-gate 	 * only if the user did not automatically authenticate.
3027*7c478bd9Sstevel@tonic-gate 	 */
3028*7c478bd9Sstevel@tonic-gate 	if (getenv("USER") == '\0' && auth_status < AUTH_USER)
3029*7c478bd9Sstevel@tonic-gate 		showbanner();
3030*7c478bd9Sstevel@tonic-gate 
3031*7c478bd9Sstevel@tonic-gate 	/*
3032*7c478bd9Sstevel@tonic-gate 	 * If the user automatically authenticated with Kerberos
3033*7c478bd9Sstevel@tonic-gate 	 * we must set the service name that PAM will use.  We
3034*7c478bd9Sstevel@tonic-gate 	 * need to do it BEFORE the child fork so that 'cleanup'
3035*7c478bd9Sstevel@tonic-gate 	 * in the parent can call the PAM cleanup stuff with the
3036*7c478bd9Sstevel@tonic-gate 	 * same PAM service that /bin/login will use to authenticate
3037*7c478bd9Sstevel@tonic-gate 	 * this session.
3038*7c478bd9Sstevel@tonic-gate 	 */
3039*7c478bd9Sstevel@tonic-gate 	if (auth_level >= 0 && auth_status >= AUTH_USER &&
3040*7c478bd9Sstevel@tonic-gate 	    (AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) {
3041*7c478bd9Sstevel@tonic-gate 		(void) strcpy(pam_svc_name, "ktelnet");
3042*7c478bd9Sstevel@tonic-gate 	}
3043*7c478bd9Sstevel@tonic-gate 	/*
3044*7c478bd9Sstevel@tonic-gate 	 * Request to do suppress go ahead.
3045*7c478bd9Sstevel@tonic-gate 	 *
3046*7c478bd9Sstevel@tonic-gate 	 * Send this before sending the TELOPT_ECHO stuff below because
3047*7c478bd9Sstevel@tonic-gate 	 * some clients (MIT KRB5 telnet) have quirky 'kludge mode' support
3048*7c478bd9Sstevel@tonic-gate 	 * that has them turn off local echo mode if SGA is not received first.
3049*7c478bd9Sstevel@tonic-gate 	 * This also has the odd side-effect of causing the client to enable
3050*7c478bd9Sstevel@tonic-gate 	 * encryption and then immediately disable it during the ECHO option
3051*7c478bd9Sstevel@tonic-gate 	 * negotiations.  Its just better to to SGA first now that we support
3052*7c478bd9Sstevel@tonic-gate 	 * encryption.
3053*7c478bd9Sstevel@tonic-gate 	 */
3054*7c478bd9Sstevel@tonic-gate 	if (!myopts[TELOPT_SGA]) {
3055*7c478bd9Sstevel@tonic-gate 	    dooption(TELOPT_SGA);
3056*7c478bd9Sstevel@tonic-gate 	}
3057*7c478bd9Sstevel@tonic-gate 
3058*7c478bd9Sstevel@tonic-gate 	/*
3059*7c478bd9Sstevel@tonic-gate 	 * Pretend we got a DO ECHO from the client if we have not
3060*7c478bd9Sstevel@tonic-gate 	 * yet negotiated the ECHO.
3061*7c478bd9Sstevel@tonic-gate 	 */
3062*7c478bd9Sstevel@tonic-gate 	if (!myopts[TELOPT_ECHO]) {
3063*7c478bd9Sstevel@tonic-gate 	    dooption(TELOPT_ECHO);
3064*7c478bd9Sstevel@tonic-gate 	}
3065*7c478bd9Sstevel@tonic-gate 
3066*7c478bd9Sstevel@tonic-gate 	/*
3067*7c478bd9Sstevel@tonic-gate 	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
3068*7c478bd9Sstevel@tonic-gate 	 * because 4.2 clients are unable to deal with TCP urgent data.
3069*7c478bd9Sstevel@tonic-gate 	 *
3070*7c478bd9Sstevel@tonic-gate 	 * To find out, we send out a "DO ECHO".  If the remote system
3071*7c478bd9Sstevel@tonic-gate 	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
3072*7c478bd9Sstevel@tonic-gate 	 * that fact ("WILL ECHO" ==> that the client will echo what
3073*7c478bd9Sstevel@tonic-gate 	 * WE, the server, sends it; it does NOT mean that the client will
3074*7c478bd9Sstevel@tonic-gate 	 * echo the terminal input).
3075*7c478bd9Sstevel@tonic-gate 	 */
3076*7c478bd9Sstevel@tonic-gate 	send_do(TELOPT_ECHO);
3077*7c478bd9Sstevel@tonic-gate 	remopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
3078*7c478bd9Sstevel@tonic-gate 
3079*7c478bd9Sstevel@tonic-gate 	if ((pid = fork()) < 0)
3080*7c478bd9Sstevel@tonic-gate 		fatalperror(netfd, "fork", errno);
3081*7c478bd9Sstevel@tonic-gate 	if (pid)
3082*7c478bd9Sstevel@tonic-gate 		telnet(net, master);
3083*7c478bd9Sstevel@tonic-gate 	/*
3084*7c478bd9Sstevel@tonic-gate 	 * The child process needs to be the session leader
3085*7c478bd9Sstevel@tonic-gate 	 * and have the pty as its controlling tty.  Thus we need
3086*7c478bd9Sstevel@tonic-gate 	 * to re-open the slave side of the pty no without
3087*7c478bd9Sstevel@tonic-gate 	 * the O_NOCTTY flag that we have been careful to
3088*7c478bd9Sstevel@tonic-gate 	 * use up to this point.
3089*7c478bd9Sstevel@tonic-gate 	 */
3090*7c478bd9Sstevel@tonic-gate 	(void) setsid();
3091*7c478bd9Sstevel@tonic-gate 
3092*7c478bd9Sstevel@tonic-gate 	tt = open(line, O_RDWR);
3093*7c478bd9Sstevel@tonic-gate 	if (tt < 0)
3094*7c478bd9Sstevel@tonic-gate 		fatalperror(netfd, line, errno);
3095*7c478bd9Sstevel@tonic-gate 	(void) close(netfd);
3096*7c478bd9Sstevel@tonic-gate 	(void) close(ptmfd);
3097*7c478bd9Sstevel@tonic-gate 	(void) close(f);
3098*7c478bd9Sstevel@tonic-gate 	(void) close(p);
3099*7c478bd9Sstevel@tonic-gate 	(void) close(t);
3100*7c478bd9Sstevel@tonic-gate 	if (tt != 0)
3101*7c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 0);
3102*7c478bd9Sstevel@tonic-gate 	if (tt != 1)
3103*7c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 1);
3104*7c478bd9Sstevel@tonic-gate 	if (tt != 2)
3105*7c478bd9Sstevel@tonic-gate 		(void) dup2(tt, 2);
3106*7c478bd9Sstevel@tonic-gate 	if (tt > 2)
3107*7c478bd9Sstevel@tonic-gate 		(void) close(tt);
3108*7c478bd9Sstevel@tonic-gate 
3109*7c478bd9Sstevel@tonic-gate 	if (terminaltype)
3110*7c478bd9Sstevel@tonic-gate 		(void) local_setenv("TERM", terminaltype+5, 1);
3111*7c478bd9Sstevel@tonic-gate 	/*
3112*7c478bd9Sstevel@tonic-gate 	 * 	-h : pass on name of host.
3113*7c478bd9Sstevel@tonic-gate 	 *		WARNING:  -h is accepted by login if and only if
3114*7c478bd9Sstevel@tonic-gate 	 *			getuid() == 0.
3115*7c478bd9Sstevel@tonic-gate 	 * 	-p : don't clobber the environment (so terminal type stays set).
3116*7c478bd9Sstevel@tonic-gate 	 */
3117*7c478bd9Sstevel@tonic-gate 	{
3118*7c478bd9Sstevel@tonic-gate 		/* System V login expects a utmp entry to already be there */
3119*7c478bd9Sstevel@tonic-gate 		struct utmpx ut;
3120*7c478bd9Sstevel@tonic-gate 		(void) memset((char *)&ut, 0, sizeof (ut));
3121*7c478bd9Sstevel@tonic-gate 		(void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
3122*7c478bd9Sstevel@tonic-gate 		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
3123*7c478bd9Sstevel@tonic-gate 		ut.ut_pid = getpid();
3124*7c478bd9Sstevel@tonic-gate 		ut.ut_id[0] = 't';
3125*7c478bd9Sstevel@tonic-gate 		ut.ut_id[1] = (char)SC_WILDC;
3126*7c478bd9Sstevel@tonic-gate 		ut.ut_id[2] = (char)SC_WILDC;
3127*7c478bd9Sstevel@tonic-gate 		ut.ut_id[3] = (char)SC_WILDC;
3128*7c478bd9Sstevel@tonic-gate 		ut.ut_type = LOGIN_PROCESS;
3129*7c478bd9Sstevel@tonic-gate 		ut.ut_exit.e_termination = 0;
3130*7c478bd9Sstevel@tonic-gate 		ut.ut_exit.e_exit = 0;
3131*7c478bd9Sstevel@tonic-gate 		(void) time(&ut.ut_tv.tv_sec);
3132*7c478bd9Sstevel@tonic-gate 		if (makeutx(&ut) == NULL)
3133*7c478bd9Sstevel@tonic-gate 			syslog(LOG_INFO, "in.telnetd:\tmakeutx failed");
3134*7c478bd9Sstevel@tonic-gate 	}
3135*7c478bd9Sstevel@tonic-gate 
3136*7c478bd9Sstevel@tonic-gate 	/*
3137*7c478bd9Sstevel@tonic-gate 	 * Load in the cached environment variables and either
3138*7c478bd9Sstevel@tonic-gate 	 * set/unset them in the environment.
3139*7c478bd9Sstevel@tonic-gate 	 */
3140*7c478bd9Sstevel@tonic-gate 	for (next = envlist_head; next; ) {
3141*7c478bd9Sstevel@tonic-gate 		env = next;
3142*7c478bd9Sstevel@tonic-gate 		if (env->delete)
3143*7c478bd9Sstevel@tonic-gate 			(void) local_unsetenv(env->name);
3144*7c478bd9Sstevel@tonic-gate 		else
3145*7c478bd9Sstevel@tonic-gate 			(void) local_setenv(env->name, env->value, 1);
3146*7c478bd9Sstevel@tonic-gate 		free(env->name);
3147*7c478bd9Sstevel@tonic-gate 		free(env->value);
3148*7c478bd9Sstevel@tonic-gate 		next = env->next;
3149*7c478bd9Sstevel@tonic-gate 		free(env);
3150*7c478bd9Sstevel@tonic-gate 	}
3151*7c478bd9Sstevel@tonic-gate 
3152*7c478bd9Sstevel@tonic-gate 	if (!username || !username[0])
3153*7c478bd9Sstevel@tonic-gate 		auth_status = AUTH_REJECT; /* we dont know who this is */
3154*7c478bd9Sstevel@tonic-gate 
3155*7c478bd9Sstevel@tonic-gate 	/* If the current auth status is less than the required level, exit */
3156*7c478bd9Sstevel@tonic-gate 	if (auth_status < auth_level) {
3157*7c478bd9Sstevel@tonic-gate 		fatal(net, "Authentication failed\n");
3158*7c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
3159*7c478bd9Sstevel@tonic-gate 	}
3160*7c478bd9Sstevel@tonic-gate 
3161*7c478bd9Sstevel@tonic-gate 	/*
3162*7c478bd9Sstevel@tonic-gate 	 * If AUTH_VALID (proper authentication REQUIRED and we have
3163*7c478bd9Sstevel@tonic-gate 	 * a krb5_name), exec '/bin/login', make sure it uses the
3164*7c478bd9Sstevel@tonic-gate 	 * correct PAM service name (pam_svc_name). If possible,
3165*7c478bd9Sstevel@tonic-gate 	 * make sure the krb5 authenticated user's name (krb5_name)
3166*7c478bd9Sstevel@tonic-gate 	 * is in the PAM REPOSITORY for krb5.
3167*7c478bd9Sstevel@tonic-gate 	 */
3168*7c478bd9Sstevel@tonic-gate 	if (auth_level >= 0 &&
3169*7c478bd9Sstevel@tonic-gate 	    (auth_status == AUTH_VALID || auth_status == AUTH_USER) &&
3170*7c478bd9Sstevel@tonic-gate 	    ((krb5_name != NULL) && strlen(krb5_name)) &&
3171*7c478bd9Sstevel@tonic-gate 	    ((AuthenticatingUser != NULL) && strlen(AuthenticatingUser))) {
3172*7c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
3173*7c478bd9Sstevel@tonic-gate 			    "-p",
3174*7c478bd9Sstevel@tonic-gate 			    "-d", slavename,
3175*7c478bd9Sstevel@tonic-gate 			    "-h", host,
3176*7c478bd9Sstevel@tonic-gate 			    "-u", krb5_name,
3177*7c478bd9Sstevel@tonic-gate 			    "-s", pam_svc_name,
3178*7c478bd9Sstevel@tonic-gate 			    "-R", KRB5_REPOSITORY_NAME,
3179*7c478bd9Sstevel@tonic-gate 			    AuthenticatingUser, 0);
3180*7c478bd9Sstevel@tonic-gate 	} else if (auth_level >= 0 &&
3181*7c478bd9Sstevel@tonic-gate 		auth_status >= AUTH_USER &&
3182*7c478bd9Sstevel@tonic-gate 		(((AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) ||
3183*7c478bd9Sstevel@tonic-gate 		getenv("USER"))) {
3184*7c478bd9Sstevel@tonic-gate 		/*
3185*7c478bd9Sstevel@tonic-gate 		 * If we only know the name but not the principal,
3186*7c478bd9Sstevel@tonic-gate 		 * login will have to authenticate further.
3187*7c478bd9Sstevel@tonic-gate 		 */
3188*7c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
3189*7c478bd9Sstevel@tonic-gate 			    "-p",
3190*7c478bd9Sstevel@tonic-gate 			    "-d", slavename,
3191*7c478bd9Sstevel@tonic-gate 			    "-h", host,
3192*7c478bd9Sstevel@tonic-gate 			    "-s", pam_svc_name,
3193*7c478bd9Sstevel@tonic-gate 			    (AuthenticatingUser != NULL ? AuthenticatingUser :
3194*7c478bd9Sstevel@tonic-gate 			    getenv("USER")),
3195*7c478bd9Sstevel@tonic-gate 			    0);
3196*7c478bd9Sstevel@tonic-gate 
3197*7c478bd9Sstevel@tonic-gate 	} else /* default, no auth. info available, login does it all */ {
3198*7c478bd9Sstevel@tonic-gate 		(void) execl(LOGIN_PROGRAM, "login",
3199*7c478bd9Sstevel@tonic-gate 			    "-p", "-h", host, "-d", slavename,
3200*7c478bd9Sstevel@tonic-gate 			    getenv("USER"), 0);
3201*7c478bd9Sstevel@tonic-gate 	}
3202*7c478bd9Sstevel@tonic-gate 
3203*7c478bd9Sstevel@tonic-gate 	fatalperror(netfd, LOGIN_PROGRAM, errno);
3204*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
3205*7c478bd9Sstevel@tonic-gate }
3206*7c478bd9Sstevel@tonic-gate 
3207*7c478bd9Sstevel@tonic-gate static void
3208*7c478bd9Sstevel@tonic-gate fatal(int f, char *msg)
3209*7c478bd9Sstevel@tonic-gate {
3210*7c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
3211*7c478bd9Sstevel@tonic-gate 
3212*7c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "telnetd: %s.\r\n", msg);
3213*7c478bd9Sstevel@tonic-gate 	(void) write(f, buf, strlen(buf));
3214*7c478bd9Sstevel@tonic-gate 	exit(EXIT_FAILURE);
3215*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
3216*7c478bd9Sstevel@tonic-gate }
3217*7c478bd9Sstevel@tonic-gate 
3218*7c478bd9Sstevel@tonic-gate static void
3219*7c478bd9Sstevel@tonic-gate fatalperror(int f, char *msg, int errnum)
3220*7c478bd9Sstevel@tonic-gate {
3221*7c478bd9Sstevel@tonic-gate 	char buf[BUFSIZ];
3222*7c478bd9Sstevel@tonic-gate 
3223*7c478bd9Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
3224*7c478bd9Sstevel@tonic-gate 			"%s: %s\r\n", msg, strerror(errnum));
3225*7c478bd9Sstevel@tonic-gate 	fatal(f, buf);
3226*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
3227*7c478bd9Sstevel@tonic-gate }
3228*7c478bd9Sstevel@tonic-gate 
3229*7c478bd9Sstevel@tonic-gate /*
3230*7c478bd9Sstevel@tonic-gate  * Main loop.  Select from pty and network, and
3231*7c478bd9Sstevel@tonic-gate  * hand data to telnet receiver finite state machine
3232*7c478bd9Sstevel@tonic-gate  * when it receives telnet protocol. Regular data
3233*7c478bd9Sstevel@tonic-gate  * flow between pty and network takes place through
3234*7c478bd9Sstevel@tonic-gate  * inkernel telnet streams module (telmod).
3235*7c478bd9Sstevel@tonic-gate  */
3236*7c478bd9Sstevel@tonic-gate static void
3237*7c478bd9Sstevel@tonic-gate telnet(int net, int master)
3238*7c478bd9Sstevel@tonic-gate {
3239*7c478bd9Sstevel@tonic-gate 	int on = 1;
3240*7c478bd9Sstevel@tonic-gate 	char mode;
3241*7c478bd9Sstevel@tonic-gate 	struct	strioctl	telnetmod;
3242*7c478bd9Sstevel@tonic-gate 	int	nsize = 0;
3243*7c478bd9Sstevel@tonic-gate 	char	binary_in = 0;
3244*7c478bd9Sstevel@tonic-gate 	char binary_out = 0;
3245*7c478bd9Sstevel@tonic-gate 
3246*7c478bd9Sstevel@tonic-gate 	if (ioctl(net, FIONBIO, &on) == -1)
3247*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl FIONBIO net: %m\n");
3248*7c478bd9Sstevel@tonic-gate 	if (ioctl(master, FIONBIO, &on) == -1)
3249*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl FIONBIO pty p: %m\n");
3250*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGTSTP, SIG_IGN);
3251*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, (void (*)())cleanup);
3252*7c478bd9Sstevel@tonic-gate 	(void) setpgrp();
3253*7c478bd9Sstevel@tonic-gate 
3254*7c478bd9Sstevel@tonic-gate 	/*
3255*7c478bd9Sstevel@tonic-gate 	 * Call telrcv() once to pick up anything received during
3256*7c478bd9Sstevel@tonic-gate 	 * terminal type negotiation.
3257*7c478bd9Sstevel@tonic-gate 	 */
3258*7c478bd9Sstevel@tonic-gate 	telrcv();
3259*7c478bd9Sstevel@tonic-gate 
3260*7c478bd9Sstevel@tonic-gate 	netflush();
3261*7c478bd9Sstevel@tonic-gate 	ptyflush();
3262*7c478bd9Sstevel@tonic-gate 
3263*7c478bd9Sstevel@tonic-gate 	for (;;) {
3264*7c478bd9Sstevel@tonic-gate 		fd_set ibits, obits, xbits;
3265*7c478bd9Sstevel@tonic-gate 		int c;
3266*7c478bd9Sstevel@tonic-gate 
3267*7c478bd9Sstevel@tonic-gate 		if (ncc < 0)
3268*7c478bd9Sstevel@tonic-gate 			break;
3269*7c478bd9Sstevel@tonic-gate 
3270*7c478bd9Sstevel@tonic-gate 		FD_ZERO(&ibits);
3271*7c478bd9Sstevel@tonic-gate 		FD_ZERO(&obits);
3272*7c478bd9Sstevel@tonic-gate 		FD_ZERO(&xbits);
3273*7c478bd9Sstevel@tonic-gate 
3274*7c478bd9Sstevel@tonic-gate 		/*
3275*7c478bd9Sstevel@tonic-gate 		 * If we couldn't flush all our output to the network,
3276*7c478bd9Sstevel@tonic-gate 		 * keep checking for when we can.
3277*7c478bd9Sstevel@tonic-gate 		 */
3278*7c478bd9Sstevel@tonic-gate 		if (nfrontp - nbackp)
3279*7c478bd9Sstevel@tonic-gate 			FD_SET(net, &obits);
3280*7c478bd9Sstevel@tonic-gate 		/*
3281*7c478bd9Sstevel@tonic-gate 		 * Never look for input if there's still
3282*7c478bd9Sstevel@tonic-gate 		 * stuff in the corresponding output buffer
3283*7c478bd9Sstevel@tonic-gate 		 */
3284*7c478bd9Sstevel@tonic-gate 		if (pfrontp - pbackp) {
3285*7c478bd9Sstevel@tonic-gate 			FD_SET(master, &obits);
3286*7c478bd9Sstevel@tonic-gate 		} else {
3287*7c478bd9Sstevel@tonic-gate 			FD_SET(net, &ibits);
3288*7c478bd9Sstevel@tonic-gate 		}
3289*7c478bd9Sstevel@tonic-gate 		if (!SYNCHing) {
3290*7c478bd9Sstevel@tonic-gate 			FD_SET(net, &xbits);
3291*7c478bd9Sstevel@tonic-gate 		}
3292*7c478bd9Sstevel@tonic-gate 
3293*7c478bd9Sstevel@tonic-gate #define	max(x, y)	(((x) < (y)) ? (y) : (x))
3294*7c478bd9Sstevel@tonic-gate 
3295*7c478bd9Sstevel@tonic-gate 		/*
3296*7c478bd9Sstevel@tonic-gate 		 * make an ioctl to telnet module (net side) to send
3297*7c478bd9Sstevel@tonic-gate 		 * binary mode of telnet daemon. binary_in and
3298*7c478bd9Sstevel@tonic-gate 		 * binary_out are 0 if not in binary mode.
3299*7c478bd9Sstevel@tonic-gate 		 */
3300*7c478bd9Sstevel@tonic-gate 		if (binary_in != myopts[TELOPT_BINARY] ||
3301*7c478bd9Sstevel@tonic-gate 		    binary_out != remopts[TELOPT_BINARY]) {
3302*7c478bd9Sstevel@tonic-gate 
3303*7c478bd9Sstevel@tonic-gate 			mode = 0;
3304*7c478bd9Sstevel@tonic-gate 			if (myopts[TELOPT_BINARY] != OPT_NO)
3305*7c478bd9Sstevel@tonic-gate 				mode |= TEL_BINARY_IN;
3306*7c478bd9Sstevel@tonic-gate 
3307*7c478bd9Sstevel@tonic-gate 			if (remopts[TELOPT_BINARY] != OPT_NO)
3308*7c478bd9Sstevel@tonic-gate 				mode |= TEL_BINARY_OUT;
3309*7c478bd9Sstevel@tonic-gate 
3310*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_cmd = TEL_IOC_MODE;
3311*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_timout = -1;
3312*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_len = 1;
3313*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_dp = &mode;
3314*7c478bd9Sstevel@tonic-gate 
3315*7c478bd9Sstevel@tonic-gate 			syslog(LOG_DEBUG, "TEL_IOC_MODE binary has changed\n");
3316*7c478bd9Sstevel@tonic-gate 
3317*7c478bd9Sstevel@tonic-gate 			if (ioctl(net, I_STR, &telnetmod) < 0)
3318*7c478bd9Sstevel@tonic-gate 				fatal(net, "ioctl TEL_IOC_MODE failed\n");
3319*7c478bd9Sstevel@tonic-gate 			binary_in = myopts[TELOPT_BINARY];
3320*7c478bd9Sstevel@tonic-gate 			binary_out = remopts[TELOPT_BINARY];
3321*7c478bd9Sstevel@tonic-gate 		}
3322*7c478bd9Sstevel@tonic-gate 		if (state == TS_DATA) {
3323*7c478bd9Sstevel@tonic-gate 			if ((nfrontp == nbackp) &&
3324*7c478bd9Sstevel@tonic-gate 				(pfrontp == pbackp)) {
3325*7c478bd9Sstevel@tonic-gate 				if (ioctl(net, I_NREAD, &nsize) < 0)
3326*7c478bd9Sstevel@tonic-gate 					fatalperror(net,
3327*7c478bd9Sstevel@tonic-gate 					    "ioctl I_NREAD failed\n", errno);
3328*7c478bd9Sstevel@tonic-gate 				if (nsize)
3329*7c478bd9Sstevel@tonic-gate 					drainstream(nsize);
3330*7c478bd9Sstevel@tonic-gate 
3331*7c478bd9Sstevel@tonic-gate 				/*
3332*7c478bd9Sstevel@tonic-gate 				 * make an ioctl to reinsert remaining data at
3333*7c478bd9Sstevel@tonic-gate 				 * streamhead. After this, ioctl reenables the
3334*7c478bd9Sstevel@tonic-gate 				 * telnet lower put queue. This queue was
3335*7c478bd9Sstevel@tonic-gate 				 * noenabled by telnet module after sending
3336*7c478bd9Sstevel@tonic-gate 				 * protocol/urgent data to telnetd.
3337*7c478bd9Sstevel@tonic-gate 				 */
3338*7c478bd9Sstevel@tonic-gate 
3339*7c478bd9Sstevel@tonic-gate 				telnetmod.ic_cmd = TEL_IOC_ENABLE;
3340*7c478bd9Sstevel@tonic-gate 				telnetmod.ic_timout = -1;
3341*7c478bd9Sstevel@tonic-gate 				if (ncc || nsize) {
3342*7c478bd9Sstevel@tonic-gate 					telnetmod.ic_len = ncc + nsize;
3343*7c478bd9Sstevel@tonic-gate 					telnetmod.ic_dp = netip;
3344*7c478bd9Sstevel@tonic-gate 				} else {
3345*7c478bd9Sstevel@tonic-gate 					telnetmod.ic_len = 0;
3346*7c478bd9Sstevel@tonic-gate 					telnetmod.ic_dp = NULL;
3347*7c478bd9Sstevel@tonic-gate 				}
3348*7c478bd9Sstevel@tonic-gate 				if (ioctl(net, I_STR, &telnetmod) < 0)
3349*7c478bd9Sstevel@tonic-gate 					fatal(net, "ioctl TEL_IOC_ENABLE \
3350*7c478bd9Sstevel@tonic-gate 						failed\n");
3351*7c478bd9Sstevel@tonic-gate 
3352*7c478bd9Sstevel@tonic-gate 				telmod_init_done = B_TRUE;
3353*7c478bd9Sstevel@tonic-gate 
3354*7c478bd9Sstevel@tonic-gate 				netip = netibuf;
3355*7c478bd9Sstevel@tonic-gate 				(void) memset(netibuf, 0, netibufsize);
3356*7c478bd9Sstevel@tonic-gate 
3357*7c478bd9Sstevel@tonic-gate 				ncc = 0;
3358*7c478bd9Sstevel@tonic-gate 			}
3359*7c478bd9Sstevel@tonic-gate 		} else {
3360*7c478bd9Sstevel@tonic-gate 			/*
3361*7c478bd9Sstevel@tonic-gate 			 * state not changed to TS_DATA and hence, more to read
3362*7c478bd9Sstevel@tonic-gate 			 * send ioctl to get one more message block.
3363*7c478bd9Sstevel@tonic-gate 			 */
3364*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_cmd = TEL_IOC_GETBLK;
3365*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_timout = -1;
3366*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_len = 0;
3367*7c478bd9Sstevel@tonic-gate 			telnetmod.ic_dp = NULL;
3368*7c478bd9Sstevel@tonic-gate 
3369*7c478bd9Sstevel@tonic-gate 			if (ioctl(net, I_STR, &telnetmod) < 0)
3370*7c478bd9Sstevel@tonic-gate 				fatal(net, "ioctl TEL_IOC_GETBLK failed\n");
3371*7c478bd9Sstevel@tonic-gate 		}
3372*7c478bd9Sstevel@tonic-gate 
3373*7c478bd9Sstevel@tonic-gate 		if ((c = select(max(net, master) + 1, &ibits, &obits, &xbits,
3374*7c478bd9Sstevel@tonic-gate 		    (struct timeval *)0)) < 1) {
3375*7c478bd9Sstevel@tonic-gate 			if (c == -1) {
3376*7c478bd9Sstevel@tonic-gate 				if (errno == EINTR) {
3377*7c478bd9Sstevel@tonic-gate 					continue;
3378*7c478bd9Sstevel@tonic-gate 				}
3379*7c478bd9Sstevel@tonic-gate 			}
3380*7c478bd9Sstevel@tonic-gate 			(void) sleep(5);
3381*7c478bd9Sstevel@tonic-gate 			continue;
3382*7c478bd9Sstevel@tonic-gate 		}
3383*7c478bd9Sstevel@tonic-gate 
3384*7c478bd9Sstevel@tonic-gate 		/*
3385*7c478bd9Sstevel@tonic-gate 		 * Any urgent data?
3386*7c478bd9Sstevel@tonic-gate 		 */
3387*7c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &xbits)) {
3388*7c478bd9Sstevel@tonic-gate 			SYNCHing = 1;
3389*7c478bd9Sstevel@tonic-gate 		}
3390*7c478bd9Sstevel@tonic-gate 
3391*7c478bd9Sstevel@tonic-gate 		/*
3392*7c478bd9Sstevel@tonic-gate 		 * Something to read from the network...
3393*7c478bd9Sstevel@tonic-gate 		 */
3394*7c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &ibits)) {
3395*7c478bd9Sstevel@tonic-gate 		    ncc = read(net, netibuf, netibufsize);
3396*7c478bd9Sstevel@tonic-gate 		    if (ncc < 0 && errno == EWOULDBLOCK)
3397*7c478bd9Sstevel@tonic-gate 			ncc = 0;
3398*7c478bd9Sstevel@tonic-gate 		    else {
3399*7c478bd9Sstevel@tonic-gate 			if (ncc <= 0) {
3400*7c478bd9Sstevel@tonic-gate 			    break;
3401*7c478bd9Sstevel@tonic-gate 			}
3402*7c478bd9Sstevel@tonic-gate 			netip = netibuf;
3403*7c478bd9Sstevel@tonic-gate 		    }
3404*7c478bd9Sstevel@tonic-gate 		}
3405*7c478bd9Sstevel@tonic-gate 
3406*7c478bd9Sstevel@tonic-gate 		if (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)
3407*7c478bd9Sstevel@tonic-gate 			netflush();
3408*7c478bd9Sstevel@tonic-gate 		if (ncc > 0)
3409*7c478bd9Sstevel@tonic-gate 			telrcv();
3410*7c478bd9Sstevel@tonic-gate 		if (FD_ISSET(master, &obits) && (pfrontp - pbackp) > 0)
3411*7c478bd9Sstevel@tonic-gate 			ptyflush();
3412*7c478bd9Sstevel@tonic-gate 	}
3413*7c478bd9Sstevel@tonic-gate 	cleanup(0);
3414*7c478bd9Sstevel@tonic-gate }
3415*7c478bd9Sstevel@tonic-gate 
3416*7c478bd9Sstevel@tonic-gate static void
3417*7c478bd9Sstevel@tonic-gate telrcv(void)
3418*7c478bd9Sstevel@tonic-gate {
3419*7c478bd9Sstevel@tonic-gate 	int c;
3420*7c478bd9Sstevel@tonic-gate 
3421*7c478bd9Sstevel@tonic-gate 	while (ncc > 0) {
3422*7c478bd9Sstevel@tonic-gate 		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
3423*7c478bd9Sstevel@tonic-gate 			return;
3424*7c478bd9Sstevel@tonic-gate 		c = *netip & 0377;
3425*7c478bd9Sstevel@tonic-gate 		/*
3426*7c478bd9Sstevel@tonic-gate 		 * Once we hit data, we want to transition back to
3427*7c478bd9Sstevel@tonic-gate 		 * in-kernel processing.  However, this code is shared
3428*7c478bd9Sstevel@tonic-gate 		 * by getterminaltype()/ttloop() which run before the
3429*7c478bd9Sstevel@tonic-gate 		 * in-kernel plumbing is available.  So if we are still
3430*7c478bd9Sstevel@tonic-gate 		 * processing the initial option negotiation, even TS_DATA
3431*7c478bd9Sstevel@tonic-gate 		 * must be processed here.
3432*7c478bd9Sstevel@tonic-gate 		 */
3433*7c478bd9Sstevel@tonic-gate 		if (c != IAC && state == TS_DATA && init_neg_done) {
3434*7c478bd9Sstevel@tonic-gate 			break;
3435*7c478bd9Sstevel@tonic-gate 		}
3436*7c478bd9Sstevel@tonic-gate 		netip++;
3437*7c478bd9Sstevel@tonic-gate 		ncc--;
3438*7c478bd9Sstevel@tonic-gate 		switch (state) {
3439*7c478bd9Sstevel@tonic-gate 
3440*7c478bd9Sstevel@tonic-gate 		case TS_CR:
3441*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3442*7c478bd9Sstevel@tonic-gate 			/* Strip off \n or \0 after a \r */
3443*7c478bd9Sstevel@tonic-gate 			if ((c == 0) || (c == '\n')) {
3444*7c478bd9Sstevel@tonic-gate 				break;
3445*7c478bd9Sstevel@tonic-gate 			}
3446*7c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
3447*7c478bd9Sstevel@tonic-gate 
3448*7c478bd9Sstevel@tonic-gate 		case TS_DATA:
3449*7c478bd9Sstevel@tonic-gate 			if (c == IAC) {
3450*7c478bd9Sstevel@tonic-gate 				state = TS_IAC;
3451*7c478bd9Sstevel@tonic-gate 				break;
3452*7c478bd9Sstevel@tonic-gate 			}
3453*7c478bd9Sstevel@tonic-gate 			if (inter > 0)
3454*7c478bd9Sstevel@tonic-gate 				break;
3455*7c478bd9Sstevel@tonic-gate 			/*
3456*7c478bd9Sstevel@tonic-gate 			 * We map \r\n ==> \r, since
3457*7c478bd9Sstevel@tonic-gate 			 * We now map \r\n ==> \r for pragmatic reasons.
3458*7c478bd9Sstevel@tonic-gate 			 * Many client implementations send \r\n when
3459*7c478bd9Sstevel@tonic-gate 			 * the user hits the CarriageReturn key.
3460*7c478bd9Sstevel@tonic-gate 			 *
3461*7c478bd9Sstevel@tonic-gate 			 * We USED to map \r\n ==> \n, since \r\n says
3462*7c478bd9Sstevel@tonic-gate 			 * that we want to be in column 1 of the next
3463*7c478bd9Sstevel@tonic-gate 			 * line.
3464*7c478bd9Sstevel@tonic-gate 			 */
3465*7c478bd9Sstevel@tonic-gate 			if (c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
3466*7c478bd9Sstevel@tonic-gate 				state = TS_CR;
3467*7c478bd9Sstevel@tonic-gate 			}
3468*7c478bd9Sstevel@tonic-gate 			*pfrontp++ = c;
3469*7c478bd9Sstevel@tonic-gate 			break;
3470*7c478bd9Sstevel@tonic-gate 
3471*7c478bd9Sstevel@tonic-gate 		case TS_IAC:
3472*7c478bd9Sstevel@tonic-gate 			switch (c) {
3473*7c478bd9Sstevel@tonic-gate 
3474*7c478bd9Sstevel@tonic-gate 			/*
3475*7c478bd9Sstevel@tonic-gate 			 * Send the process on the pty side an
3476*7c478bd9Sstevel@tonic-gate 			 * interrupt.  Do this with a NULL or
3477*7c478bd9Sstevel@tonic-gate 			 * interrupt char; depending on the tty mode.
3478*7c478bd9Sstevel@tonic-gate 			 */
3479*7c478bd9Sstevel@tonic-gate 			case IP:
3480*7c478bd9Sstevel@tonic-gate 				interrupt();
3481*7c478bd9Sstevel@tonic-gate 				break;
3482*7c478bd9Sstevel@tonic-gate 
3483*7c478bd9Sstevel@tonic-gate 			case BREAK:
3484*7c478bd9Sstevel@tonic-gate 				sendbrk();
3485*7c478bd9Sstevel@tonic-gate 				break;
3486*7c478bd9Sstevel@tonic-gate 
3487*7c478bd9Sstevel@tonic-gate 			/*
3488*7c478bd9Sstevel@tonic-gate 			 * Are You There?
3489*7c478bd9Sstevel@tonic-gate 			 */
3490*7c478bd9Sstevel@tonic-gate 			case AYT:
3491*7c478bd9Sstevel@tonic-gate 				write_data_len("\r\n[Yes]\r\n", 9);
3492*7c478bd9Sstevel@tonic-gate 				break;
3493*7c478bd9Sstevel@tonic-gate 
3494*7c478bd9Sstevel@tonic-gate 			/*
3495*7c478bd9Sstevel@tonic-gate 			 * Abort Output
3496*7c478bd9Sstevel@tonic-gate 			 */
3497*7c478bd9Sstevel@tonic-gate 			case AO: {
3498*7c478bd9Sstevel@tonic-gate 					struct ltchars tmpltc;
3499*7c478bd9Sstevel@tonic-gate 
3500*7c478bd9Sstevel@tonic-gate 					ptyflush();	/* half-hearted */
3501*7c478bd9Sstevel@tonic-gate 					if (ioctl(pty, TIOCGLTC, &tmpltc) == -1)
3502*7c478bd9Sstevel@tonic-gate 						syslog(LOG_INFO,
3503*7c478bd9Sstevel@tonic-gate 						    "ioctl TIOCGLTC: %m\n");
3504*7c478bd9Sstevel@tonic-gate 					if (tmpltc.t_flushc != '\377') {
3505*7c478bd9Sstevel@tonic-gate 						*pfrontp++ = tmpltc.t_flushc;
3506*7c478bd9Sstevel@tonic-gate 					}
3507*7c478bd9Sstevel@tonic-gate 					netclear();	/* clear buffer back */
3508*7c478bd9Sstevel@tonic-gate 					write_data("%c%c", (uchar_t)IAC,
3509*7c478bd9Sstevel@tonic-gate 						(uchar_t)DM);
3510*7c478bd9Sstevel@tonic-gate 
3511*7c478bd9Sstevel@tonic-gate 					neturg = nfrontp-1; /* off by one XXX */
3512*7c478bd9Sstevel@tonic-gate 					netflush();
3513*7c478bd9Sstevel@tonic-gate 					netflush(); /* XXX.sparker */
3514*7c478bd9Sstevel@tonic-gate 					break;
3515*7c478bd9Sstevel@tonic-gate 				}
3516*7c478bd9Sstevel@tonic-gate 
3517*7c478bd9Sstevel@tonic-gate 			/*
3518*7c478bd9Sstevel@tonic-gate 			 * Erase Character and
3519*7c478bd9Sstevel@tonic-gate 			 * Erase Line
3520*7c478bd9Sstevel@tonic-gate 			 */
3521*7c478bd9Sstevel@tonic-gate 			case EC:
3522*7c478bd9Sstevel@tonic-gate 			case EL: {
3523*7c478bd9Sstevel@tonic-gate 					struct sgttyb b;
3524*7c478bd9Sstevel@tonic-gate 					char ch;
3525*7c478bd9Sstevel@tonic-gate 
3526*7c478bd9Sstevel@tonic-gate 					ptyflush();	/* half-hearted */
3527*7c478bd9Sstevel@tonic-gate 					if (ioctl(pty, TIOCGETP, &b) == -1)
3528*7c478bd9Sstevel@tonic-gate 						syslog(LOG_INFO,
3529*7c478bd9Sstevel@tonic-gate 						    "ioctl TIOCGETP: %m\n");
3530*7c478bd9Sstevel@tonic-gate 					ch = (c == EC) ?
3531*7c478bd9Sstevel@tonic-gate 						b.sg_erase : b.sg_kill;
3532*7c478bd9Sstevel@tonic-gate 					if (ch != '\377') {
3533*7c478bd9Sstevel@tonic-gate 						*pfrontp++ = ch;
3534*7c478bd9Sstevel@tonic-gate 					}
3535*7c478bd9Sstevel@tonic-gate 					break;
3536*7c478bd9Sstevel@tonic-gate 				}
3537*7c478bd9Sstevel@tonic-gate 
3538*7c478bd9Sstevel@tonic-gate 			/*
3539*7c478bd9Sstevel@tonic-gate 			 * Check for urgent data...
3540*7c478bd9Sstevel@tonic-gate 			 */
3541*7c478bd9Sstevel@tonic-gate 			case DM:
3542*7c478bd9Sstevel@tonic-gate 				break;
3543*7c478bd9Sstevel@tonic-gate 
3544*7c478bd9Sstevel@tonic-gate 			/*
3545*7c478bd9Sstevel@tonic-gate 			 * Begin option subnegotiation...
3546*7c478bd9Sstevel@tonic-gate 			 */
3547*7c478bd9Sstevel@tonic-gate 			case SB:
3548*7c478bd9Sstevel@tonic-gate 				state = TS_SB;
3549*7c478bd9Sstevel@tonic-gate 				SB_CLEAR();
3550*7c478bd9Sstevel@tonic-gate 				continue;
3551*7c478bd9Sstevel@tonic-gate 
3552*7c478bd9Sstevel@tonic-gate 			case WILL:
3553*7c478bd9Sstevel@tonic-gate 				state = TS_WILL;
3554*7c478bd9Sstevel@tonic-gate 				continue;
3555*7c478bd9Sstevel@tonic-gate 
3556*7c478bd9Sstevel@tonic-gate 			case WONT:
3557*7c478bd9Sstevel@tonic-gate 				state = TS_WONT;
3558*7c478bd9Sstevel@tonic-gate 				continue;
3559*7c478bd9Sstevel@tonic-gate 
3560*7c478bd9Sstevel@tonic-gate 			case DO:
3561*7c478bd9Sstevel@tonic-gate 				state = TS_DO;
3562*7c478bd9Sstevel@tonic-gate 				continue;
3563*7c478bd9Sstevel@tonic-gate 
3564*7c478bd9Sstevel@tonic-gate 			case DONT:
3565*7c478bd9Sstevel@tonic-gate 				state = TS_DONT;
3566*7c478bd9Sstevel@tonic-gate 				continue;
3567*7c478bd9Sstevel@tonic-gate 
3568*7c478bd9Sstevel@tonic-gate 			case IAC:
3569*7c478bd9Sstevel@tonic-gate 				*pfrontp++ = c;
3570*7c478bd9Sstevel@tonic-gate 				break;
3571*7c478bd9Sstevel@tonic-gate 			}
3572*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3573*7c478bd9Sstevel@tonic-gate 			break;
3574*7c478bd9Sstevel@tonic-gate 		case TS_SB:
3575*7c478bd9Sstevel@tonic-gate 			if (c == IAC) {
3576*7c478bd9Sstevel@tonic-gate 				state = TS_SE;
3577*7c478bd9Sstevel@tonic-gate 			} else {
3578*7c478bd9Sstevel@tonic-gate 				SB_ACCUM(c);
3579*7c478bd9Sstevel@tonic-gate 			}
3580*7c478bd9Sstevel@tonic-gate 			break;
3581*7c478bd9Sstevel@tonic-gate 		case TS_SE:
3582*7c478bd9Sstevel@tonic-gate 			if (c != SE) {
3583*7c478bd9Sstevel@tonic-gate 				if (c != IAC) {
3584*7c478bd9Sstevel@tonic-gate 					SB_ACCUM((uchar_t)IAC);
3585*7c478bd9Sstevel@tonic-gate 				}
3586*7c478bd9Sstevel@tonic-gate 				SB_ACCUM(c);
3587*7c478bd9Sstevel@tonic-gate 				state = TS_SB;
3588*7c478bd9Sstevel@tonic-gate 
3589*7c478bd9Sstevel@tonic-gate 			} else {
3590*7c478bd9Sstevel@tonic-gate 				SB_TERM();
3591*7c478bd9Sstevel@tonic-gate 				suboption();	/* handle sub-option */
3592*7c478bd9Sstevel@tonic-gate 				state = TS_DATA;
3593*7c478bd9Sstevel@tonic-gate 			}
3594*7c478bd9Sstevel@tonic-gate 			break;
3595*7c478bd9Sstevel@tonic-gate 
3596*7c478bd9Sstevel@tonic-gate 		case TS_WILL:
3597*7c478bd9Sstevel@tonic-gate 			if (remopts[c] != OPT_YES)
3598*7c478bd9Sstevel@tonic-gate 				willoption(c);
3599*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3600*7c478bd9Sstevel@tonic-gate 			continue;
3601*7c478bd9Sstevel@tonic-gate 
3602*7c478bd9Sstevel@tonic-gate 		case TS_WONT:
3603*7c478bd9Sstevel@tonic-gate 			if (remopts[c] != OPT_NO)
3604*7c478bd9Sstevel@tonic-gate 				wontoption(c);
3605*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3606*7c478bd9Sstevel@tonic-gate 			continue;
3607*7c478bd9Sstevel@tonic-gate 
3608*7c478bd9Sstevel@tonic-gate 		case TS_DO:
3609*7c478bd9Sstevel@tonic-gate 			if (myopts[c] != OPT_YES)
3610*7c478bd9Sstevel@tonic-gate 				dooption(c);
3611*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3612*7c478bd9Sstevel@tonic-gate 			continue;
3613*7c478bd9Sstevel@tonic-gate 
3614*7c478bd9Sstevel@tonic-gate 		case TS_DONT:
3615*7c478bd9Sstevel@tonic-gate 			if (myopts[c] != OPT_NO) {
3616*7c478bd9Sstevel@tonic-gate 				dontoption(c);
3617*7c478bd9Sstevel@tonic-gate 			}
3618*7c478bd9Sstevel@tonic-gate 			state = TS_DATA;
3619*7c478bd9Sstevel@tonic-gate 			continue;
3620*7c478bd9Sstevel@tonic-gate 
3621*7c478bd9Sstevel@tonic-gate 		default:
3622*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
3623*7c478bd9Sstevel@tonic-gate 			(void) printf("telnetd: panic state=%d\n", state);
3624*7c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
3625*7c478bd9Sstevel@tonic-gate 		}
3626*7c478bd9Sstevel@tonic-gate 	}
3627*7c478bd9Sstevel@tonic-gate }
3628*7c478bd9Sstevel@tonic-gate 
3629*7c478bd9Sstevel@tonic-gate static void
3630*7c478bd9Sstevel@tonic-gate willoption(int option)
3631*7c478bd9Sstevel@tonic-gate {
3632*7c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
3633*7c478bd9Sstevel@tonic-gate 	boolean_t send_reply = B_TRUE;
3634*7c478bd9Sstevel@tonic-gate 
3635*7c478bd9Sstevel@tonic-gate 	switch (option) {
3636*7c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
3637*7c478bd9Sstevel@tonic-gate 		mode(O_RAW, 0);
3638*7c478bd9Sstevel@tonic-gate 		fmt = doopt;
3639*7c478bd9Sstevel@tonic-gate 		break;
3640*7c478bd9Sstevel@tonic-gate 
3641*7c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
3642*7c478bd9Sstevel@tonic-gate 		not42 = 0;		/* looks like a 4.2 system */
3643*7c478bd9Sstevel@tonic-gate 		/*
3644*7c478bd9Sstevel@tonic-gate 		 * Now, in a 4.2 system, to break them out of ECHOing
3645*7c478bd9Sstevel@tonic-gate 		 * (to the terminal) mode, we need to send a "WILL ECHO".
3646*7c478bd9Sstevel@tonic-gate 		 * Kludge upon kludge!
3647*7c478bd9Sstevel@tonic-gate 		 */
3648*7c478bd9Sstevel@tonic-gate 		if (myopts[TELOPT_ECHO] == OPT_YES) {
3649*7c478bd9Sstevel@tonic-gate 			dooption(TELOPT_ECHO);
3650*7c478bd9Sstevel@tonic-gate 		}
3651*7c478bd9Sstevel@tonic-gate 		fmt = dont;
3652*7c478bd9Sstevel@tonic-gate 		break;
3653*7c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE:
3654*7c478bd9Sstevel@tonic-gate 		settimer(ttypeopt);
3655*7c478bd9Sstevel@tonic-gate 		goto common;
3656*7c478bd9Sstevel@tonic-gate 
3657*7c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS:
3658*7c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
3659*7c478bd9Sstevel@tonic-gate 		goto common;
3660*7c478bd9Sstevel@tonic-gate 
3661*7c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC:
3662*7c478bd9Sstevel@tonic-gate 		settimer(xdisplocopt);
3663*7c478bd9Sstevel@tonic-gate 		goto common;
3664*7c478bd9Sstevel@tonic-gate 
3665*7c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
3666*7c478bd9Sstevel@tonic-gate 		settimer(environopt);
3667*7c478bd9Sstevel@tonic-gate 		goto common;
3668*7c478bd9Sstevel@tonic-gate 
3669*7c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
3670*7c478bd9Sstevel@tonic-gate 		settimer(authopt);
3671*7c478bd9Sstevel@tonic-gate 		if (remopts[option] == OPT_NO ||
3672*7c478bd9Sstevel@tonic-gate 		    negotiate_auth_krb5 == 0)
3673*7c478bd9Sstevel@tonic-gate 			fmt = dont;
3674*7c478bd9Sstevel@tonic-gate 		else
3675*7c478bd9Sstevel@tonic-gate 			fmt = doopt;
3676*7c478bd9Sstevel@tonic-gate 		break;
3677*7c478bd9Sstevel@tonic-gate 
3678*7c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON:
3679*7c478bd9Sstevel@tonic-gate 		settimer(oenvironopt);
3680*7c478bd9Sstevel@tonic-gate 		goto common;
3681*7c478bd9Sstevel@tonic-gate common:
3682*7c478bd9Sstevel@tonic-gate 		if (remopts[option] == OPT_YES_BUT_ALWAYS_LOOK) {
3683*7c478bd9Sstevel@tonic-gate 			remopts[option] = OPT_YES;
3684*7c478bd9Sstevel@tonic-gate 			return;
3685*7c478bd9Sstevel@tonic-gate 		}
3686*7c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
3687*7c478bd9Sstevel@tonic-gate 	case TELOPT_SGA:
3688*7c478bd9Sstevel@tonic-gate 		fmt = doopt;
3689*7c478bd9Sstevel@tonic-gate 		break;
3690*7c478bd9Sstevel@tonic-gate 
3691*7c478bd9Sstevel@tonic-gate 	case TELOPT_TM:
3692*7c478bd9Sstevel@tonic-gate 		fmt = dont;
3693*7c478bd9Sstevel@tonic-gate 		break;
3694*7c478bd9Sstevel@tonic-gate 
3695*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
3696*7c478bd9Sstevel@tonic-gate 		settimer(encropt); /* got response to do/dont */
3697*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
3698*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3699*7c478bd9Sstevel@tonic-gate 				    "RCVD IAC WILL TELOPT_ENCRYPT\n");
3700*7c478bd9Sstevel@tonic-gate 		if (krb5_privacy_allowed()) {
3701*7c478bd9Sstevel@tonic-gate 			fmt = doopt;
3702*7c478bd9Sstevel@tonic-gate 			if (sent_do_encrypt)
3703*7c478bd9Sstevel@tonic-gate 				send_reply = B_FALSE;
3704*7c478bd9Sstevel@tonic-gate 			else
3705*7c478bd9Sstevel@tonic-gate 				sent_do_encrypt = B_TRUE;
3706*7c478bd9Sstevel@tonic-gate 		} else {
3707*7c478bd9Sstevel@tonic-gate 			fmt = dont;
3708*7c478bd9Sstevel@tonic-gate 		}
3709*7c478bd9Sstevel@tonic-gate 		break;
3710*7c478bd9Sstevel@tonic-gate 
3711*7c478bd9Sstevel@tonic-gate 	default:
3712*7c478bd9Sstevel@tonic-gate 		fmt = dont;
3713*7c478bd9Sstevel@tonic-gate 		break;
3714*7c478bd9Sstevel@tonic-gate 	}
3715*7c478bd9Sstevel@tonic-gate 	if (fmt == doopt) {
3716*7c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_YES;
3717*7c478bd9Sstevel@tonic-gate 	} else {
3718*7c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_NO;
3719*7c478bd9Sstevel@tonic-gate 	}
3720*7c478bd9Sstevel@tonic-gate 	if (send_reply) {
3721*7c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
3722*7c478bd9Sstevel@tonic-gate 		netflush();
3723*7c478bd9Sstevel@tonic-gate 	}
3724*7c478bd9Sstevel@tonic-gate }
3725*7c478bd9Sstevel@tonic-gate 
3726*7c478bd9Sstevel@tonic-gate static void
3727*7c478bd9Sstevel@tonic-gate wontoption(int option)
3728*7c478bd9Sstevel@tonic-gate {
3729*7c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
3730*7c478bd9Sstevel@tonic-gate 	int send_reply = 1;
3731*7c478bd9Sstevel@tonic-gate 
3732*7c478bd9Sstevel@tonic-gate 	switch (option) {
3733*7c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
3734*7c478bd9Sstevel@tonic-gate 		not42 = 1;		/* doesn't seem to be a 4.2 system */
3735*7c478bd9Sstevel@tonic-gate 		break;
3736*7c478bd9Sstevel@tonic-gate 
3737*7c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
3738*7c478bd9Sstevel@tonic-gate 		mode(0, O_RAW);
3739*7c478bd9Sstevel@tonic-gate 		break;
3740*7c478bd9Sstevel@tonic-gate 
3741*7c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE:
3742*7c478bd9Sstevel@tonic-gate 		settimer(ttypeopt);
3743*7c478bd9Sstevel@tonic-gate 		break;
3744*7c478bd9Sstevel@tonic-gate 
3745*7c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS:
3746*7c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
3747*7c478bd9Sstevel@tonic-gate 		break;
3748*7c478bd9Sstevel@tonic-gate 
3749*7c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC:
3750*7c478bd9Sstevel@tonic-gate 		settimer(xdisplocopt);
3751*7c478bd9Sstevel@tonic-gate 		break;
3752*7c478bd9Sstevel@tonic-gate 
3753*7c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
3754*7c478bd9Sstevel@tonic-gate 		settimer(environopt);
3755*7c478bd9Sstevel@tonic-gate 		break;
3756*7c478bd9Sstevel@tonic-gate 
3757*7c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON:
3758*7c478bd9Sstevel@tonic-gate 		settimer(oenvironopt);
3759*7c478bd9Sstevel@tonic-gate 		break;
3760*7c478bd9Sstevel@tonic-gate 
3761*7c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
3762*7c478bd9Sstevel@tonic-gate 		settimer(authopt);
3763*7c478bd9Sstevel@tonic-gate 		auth_finished(0, AUTH_REJECT);
3764*7c478bd9Sstevel@tonic-gate 		if (auth_debug)
3765*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3766*7c478bd9Sstevel@tonic-gate 				    "RCVD WONT TELOPT_AUTHENTICATE\n");
3767*7c478bd9Sstevel@tonic-gate 
3768*7c478bd9Sstevel@tonic-gate 		remopts[option] = OPT_NO;
3769*7c478bd9Sstevel@tonic-gate 		send_reply = 0;
3770*7c478bd9Sstevel@tonic-gate 		break;
3771*7c478bd9Sstevel@tonic-gate 
3772*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
3773*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
3774*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3775*7c478bd9Sstevel@tonic-gate 				    "RCVD IAC WONT TELOPT_ENCRYPT\n");
3776*7c478bd9Sstevel@tonic-gate 		settimer(encropt); /* got response to will/wont */
3777*7c478bd9Sstevel@tonic-gate 		/*
3778*7c478bd9Sstevel@tonic-gate 		 * Remote side cannot send encryption. No reply necessary
3779*7c478bd9Sstevel@tonic-gate 		 * Treat this as if "IAC SB ENCRYPT END IAC SE" were
3780*7c478bd9Sstevel@tonic-gate 		 * received (RFC 2946) and disable crypto.
3781*7c478bd9Sstevel@tonic-gate 		 */
3782*7c478bd9Sstevel@tonic-gate 		encrypt_end(TELNET_DIR_DECRYPT);
3783*7c478bd9Sstevel@tonic-gate 		send_reply = 0;
3784*7c478bd9Sstevel@tonic-gate 		break;
3785*7c478bd9Sstevel@tonic-gate 	}
3786*7c478bd9Sstevel@tonic-gate 
3787*7c478bd9Sstevel@tonic-gate 	fmt = dont;
3788*7c478bd9Sstevel@tonic-gate 	remopts[option] = OPT_NO;
3789*7c478bd9Sstevel@tonic-gate 	if (send_reply) {
3790*7c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
3791*7c478bd9Sstevel@tonic-gate 	}
3792*7c478bd9Sstevel@tonic-gate }
3793*7c478bd9Sstevel@tonic-gate 
3794*7c478bd9Sstevel@tonic-gate /*
3795*7c478bd9Sstevel@tonic-gate  * We received an "IAC DO ..." message from the client, change our state
3796*7c478bd9Sstevel@tonic-gate  * to OPT_YES.
3797*7c478bd9Sstevel@tonic-gate  */
3798*7c478bd9Sstevel@tonic-gate static void
3799*7c478bd9Sstevel@tonic-gate dooption(int option)
3800*7c478bd9Sstevel@tonic-gate {
3801*7c478bd9Sstevel@tonic-gate 	uchar_t *fmt;
3802*7c478bd9Sstevel@tonic-gate 	boolean_t send_reply = B_TRUE;
3803*7c478bd9Sstevel@tonic-gate 
3804*7c478bd9Sstevel@tonic-gate 	switch (option) {
3805*7c478bd9Sstevel@tonic-gate 
3806*7c478bd9Sstevel@tonic-gate 	case TELOPT_TM:
3807*7c478bd9Sstevel@tonic-gate 		fmt = wont;
3808*7c478bd9Sstevel@tonic-gate 		break;
3809*7c478bd9Sstevel@tonic-gate 
3810*7c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
3811*7c478bd9Sstevel@tonic-gate 		mode(O_ECHO|O_CRMOD, 0);
3812*7c478bd9Sstevel@tonic-gate 		fmt = will;
3813*7c478bd9Sstevel@tonic-gate 		break;
3814*7c478bd9Sstevel@tonic-gate 
3815*7c478bd9Sstevel@tonic-gate 	case TELOPT_BINARY:
3816*7c478bd9Sstevel@tonic-gate 		mode(O_RAW, 0);
3817*7c478bd9Sstevel@tonic-gate 		fmt = will;
3818*7c478bd9Sstevel@tonic-gate 		break;
3819*7c478bd9Sstevel@tonic-gate 
3820*7c478bd9Sstevel@tonic-gate 	case TELOPT_SGA:
3821*7c478bd9Sstevel@tonic-gate 		fmt = will;
3822*7c478bd9Sstevel@tonic-gate 		break;
3823*7c478bd9Sstevel@tonic-gate 
3824*7c478bd9Sstevel@tonic-gate 	case TELOPT_LOGOUT:
3825*7c478bd9Sstevel@tonic-gate 		/*
3826*7c478bd9Sstevel@tonic-gate 		 * Options don't get much easier.  Acknowledge the option,
3827*7c478bd9Sstevel@tonic-gate 		 * and then clean up and exit.
3828*7c478bd9Sstevel@tonic-gate 		 */
3829*7c478bd9Sstevel@tonic-gate 		write_data((const char *)will, option);
3830*7c478bd9Sstevel@tonic-gate 		netflush();
3831*7c478bd9Sstevel@tonic-gate 		cleanup(0);
3832*7c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
3833*7c478bd9Sstevel@tonic-gate 
3834*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
3835*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
3836*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD DO TELOPT_ENCRYPT\n");
3837*7c478bd9Sstevel@tonic-gate 		settimer(encropt);
3838*7c478bd9Sstevel@tonic-gate 		/*
3839*7c478bd9Sstevel@tonic-gate 		 * We received a "DO".  This indicates that the other side
3840*7c478bd9Sstevel@tonic-gate 		 * wants us to encrypt our data (pending negotiatoin).
3841*7c478bd9Sstevel@tonic-gate 		 * reply with "IAC WILL ENCRYPT" if we are able to send
3842*7c478bd9Sstevel@tonic-gate 		 * encrypted data.
3843*7c478bd9Sstevel@tonic-gate 		 */
3844*7c478bd9Sstevel@tonic-gate 		if (krb5_privacy_allowed() && negotiate_encrypt) {
3845*7c478bd9Sstevel@tonic-gate 			fmt = will;
3846*7c478bd9Sstevel@tonic-gate 			if (sent_will_encrypt)
3847*7c478bd9Sstevel@tonic-gate 				send_reply = B_FALSE;
3848*7c478bd9Sstevel@tonic-gate 			else
3849*7c478bd9Sstevel@tonic-gate 				sent_will_encrypt = B_TRUE;
3850*7c478bd9Sstevel@tonic-gate 			/* return if we already sent "WILL ENCRYPT" */
3851*7c478bd9Sstevel@tonic-gate 			if (myopts[option] == OPT_YES)
3852*7c478bd9Sstevel@tonic-gate 				return;
3853*7c478bd9Sstevel@tonic-gate 		} else {
3854*7c478bd9Sstevel@tonic-gate 			fmt = wont;
3855*7c478bd9Sstevel@tonic-gate 		}
3856*7c478bd9Sstevel@tonic-gate 		break;
3857*7c478bd9Sstevel@tonic-gate 
3858*7c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
3859*7c478bd9Sstevel@tonic-gate 		if (auth_debug) {
3860*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
3861*7c478bd9Sstevel@tonic-gate 				    "RCVD DO TELOPT_AUTHENTICATION\n");
3862*7c478bd9Sstevel@tonic-gate 		}
3863*7c478bd9Sstevel@tonic-gate 		/*
3864*7c478bd9Sstevel@tonic-gate 		 * RFC 2941 - only the server can send
3865*7c478bd9Sstevel@tonic-gate 		 * "DO TELOPT_AUTHENTICATION".
3866*7c478bd9Sstevel@tonic-gate 		 * if a server receives this, it must respond with WONT...
3867*7c478bd9Sstevel@tonic-gate 		 */
3868*7c478bd9Sstevel@tonic-gate 		fmt = wont;
3869*7c478bd9Sstevel@tonic-gate 		break;
3870*7c478bd9Sstevel@tonic-gate 
3871*7c478bd9Sstevel@tonic-gate 	default:
3872*7c478bd9Sstevel@tonic-gate 		fmt = wont;
3873*7c478bd9Sstevel@tonic-gate 		break;
3874*7c478bd9Sstevel@tonic-gate 	}
3875*7c478bd9Sstevel@tonic-gate 	if (fmt == will) {
3876*7c478bd9Sstevel@tonic-gate 		myopts[option] = OPT_YES;
3877*7c478bd9Sstevel@tonic-gate 	} else {
3878*7c478bd9Sstevel@tonic-gate 		myopts[option] = OPT_NO;
3879*7c478bd9Sstevel@tonic-gate 	}
3880*7c478bd9Sstevel@tonic-gate 	if (send_reply) {
3881*7c478bd9Sstevel@tonic-gate 		write_data((const char *)fmt, option);
3882*7c478bd9Sstevel@tonic-gate 		netflush();
3883*7c478bd9Sstevel@tonic-gate 	}
3884*7c478bd9Sstevel@tonic-gate }
3885*7c478bd9Sstevel@tonic-gate 
3886*7c478bd9Sstevel@tonic-gate /*
3887*7c478bd9Sstevel@tonic-gate  * We received an "IAC DONT ..." message from client.
3888*7c478bd9Sstevel@tonic-gate  * Client does not agree with the option so act accordingly.
3889*7c478bd9Sstevel@tonic-gate  */
3890*7c478bd9Sstevel@tonic-gate static void
3891*7c478bd9Sstevel@tonic-gate dontoption(int option)
3892*7c478bd9Sstevel@tonic-gate {
3893*7c478bd9Sstevel@tonic-gate 	int send_reply = 1;
3894*7c478bd9Sstevel@tonic-gate 	switch (option) {
3895*7c478bd9Sstevel@tonic-gate 	case TELOPT_ECHO:
3896*7c478bd9Sstevel@tonic-gate 		/*
3897*7c478bd9Sstevel@tonic-gate 		 * we should stop echoing, since the client side will be doing
3898*7c478bd9Sstevel@tonic-gate 		 * it, but keep mapping CR since CR-LF will be mapped to it.
3899*7c478bd9Sstevel@tonic-gate 		 */
3900*7c478bd9Sstevel@tonic-gate 		mode(0, O_ECHO);
3901*7c478bd9Sstevel@tonic-gate 		break;
3902*7c478bd9Sstevel@tonic-gate 
3903*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT:
3904*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
3905*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD IAC DONT ENCRYPT\n");
3906*7c478bd9Sstevel@tonic-gate 		settimer(encropt);
3907*7c478bd9Sstevel@tonic-gate 		/*
3908*7c478bd9Sstevel@tonic-gate 		 * Remote side cannot receive any encrypted data,
3909*7c478bd9Sstevel@tonic-gate 		 * so dont send any.  No reply necessary.
3910*7c478bd9Sstevel@tonic-gate 		 */
3911*7c478bd9Sstevel@tonic-gate 		send_reply = 0;
3912*7c478bd9Sstevel@tonic-gate 		break;
3913*7c478bd9Sstevel@tonic-gate 
3914*7c478bd9Sstevel@tonic-gate 	default:
3915*7c478bd9Sstevel@tonic-gate 		break;
3916*7c478bd9Sstevel@tonic-gate 	}
3917*7c478bd9Sstevel@tonic-gate 
3918*7c478bd9Sstevel@tonic-gate 	myopts[option] = OPT_NO;
3919*7c478bd9Sstevel@tonic-gate 
3920*7c478bd9Sstevel@tonic-gate 	if (send_reply) {
3921*7c478bd9Sstevel@tonic-gate 		write_data((const char *)wont, option);
3922*7c478bd9Sstevel@tonic-gate 	}
3923*7c478bd9Sstevel@tonic-gate }
3924*7c478bd9Sstevel@tonic-gate 
3925*7c478bd9Sstevel@tonic-gate /*
3926*7c478bd9Sstevel@tonic-gate  * suboption()
3927*7c478bd9Sstevel@tonic-gate  *
3928*7c478bd9Sstevel@tonic-gate  *	Look at the sub-option buffer, and try to be helpful to the other
3929*7c478bd9Sstevel@tonic-gate  * side.
3930*7c478bd9Sstevel@tonic-gate  *
3931*7c478bd9Sstevel@tonic-gate  */
3932*7c478bd9Sstevel@tonic-gate static void
3933*7c478bd9Sstevel@tonic-gate suboption(void)
3934*7c478bd9Sstevel@tonic-gate {
3935*7c478bd9Sstevel@tonic-gate 	int subchar;
3936*7c478bd9Sstevel@tonic-gate 
3937*7c478bd9Sstevel@tonic-gate 	switch (subchar = SB_GET()) {
3938*7c478bd9Sstevel@tonic-gate 	case TELOPT_TTYPE: {		/* Yaaaay! */
3939*7c478bd9Sstevel@tonic-gate 		static char terminalname[5+41] = "TERM=";
3940*7c478bd9Sstevel@tonic-gate 
3941*7c478bd9Sstevel@tonic-gate 		settimer(ttypesubopt);
3942*7c478bd9Sstevel@tonic-gate 
3943*7c478bd9Sstevel@tonic-gate 		if (SB_GET() != TELQUAL_IS) {
3944*7c478bd9Sstevel@tonic-gate 			return;	/* ??? XXX but, this is the most robust */
3945*7c478bd9Sstevel@tonic-gate 		}
3946*7c478bd9Sstevel@tonic-gate 
3947*7c478bd9Sstevel@tonic-gate 		terminaltype = terminalname+strlen(terminalname);
3948*7c478bd9Sstevel@tonic-gate 
3949*7c478bd9Sstevel@tonic-gate 		while (terminaltype < (terminalname + sizeof (terminalname) -
3950*7c478bd9Sstevel@tonic-gate 		    1) && !SB_EOF()) {
3951*7c478bd9Sstevel@tonic-gate 			int c;
3952*7c478bd9Sstevel@tonic-gate 
3953*7c478bd9Sstevel@tonic-gate 			c = SB_GET();
3954*7c478bd9Sstevel@tonic-gate 			if (isupper(c)) {
3955*7c478bd9Sstevel@tonic-gate 				c = tolower(c);
3956*7c478bd9Sstevel@tonic-gate 			}
3957*7c478bd9Sstevel@tonic-gate 			*terminaltype++ = c;    /* accumulate name */
3958*7c478bd9Sstevel@tonic-gate 		}
3959*7c478bd9Sstevel@tonic-gate 		*terminaltype = 0;
3960*7c478bd9Sstevel@tonic-gate 		terminaltype = terminalname;
3961*7c478bd9Sstevel@tonic-gate 		break;
3962*7c478bd9Sstevel@tonic-gate 	}
3963*7c478bd9Sstevel@tonic-gate 
3964*7c478bd9Sstevel@tonic-gate 	case TELOPT_NAWS: {
3965*7c478bd9Sstevel@tonic-gate 		struct winsize ws;
3966*7c478bd9Sstevel@tonic-gate 
3967*7c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
3968*7c478bd9Sstevel@tonic-gate 			return;
3969*7c478bd9Sstevel@tonic-gate 		}
3970*7c478bd9Sstevel@tonic-gate 		ws.ws_col = SB_GET() << 8;
3971*7c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
3972*7c478bd9Sstevel@tonic-gate 			return;
3973*7c478bd9Sstevel@tonic-gate 		}
3974*7c478bd9Sstevel@tonic-gate 		ws.ws_col |= SB_GET();
3975*7c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
3976*7c478bd9Sstevel@tonic-gate 			return;
3977*7c478bd9Sstevel@tonic-gate 		}
3978*7c478bd9Sstevel@tonic-gate 		ws.ws_row = SB_GET() << 8;
3979*7c478bd9Sstevel@tonic-gate 		if (SB_EOF()) {
3980*7c478bd9Sstevel@tonic-gate 			return;
3981*7c478bd9Sstevel@tonic-gate 		}
3982*7c478bd9Sstevel@tonic-gate 		ws.ws_row |= SB_GET();
3983*7c478bd9Sstevel@tonic-gate 		ws.ws_xpixel = 0; ws.ws_ypixel = 0;
3984*7c478bd9Sstevel@tonic-gate 		(void) ioctl(pty, TIOCSWINSZ, &ws);
3985*7c478bd9Sstevel@tonic-gate 		settimer(nawsopt);
3986*7c478bd9Sstevel@tonic-gate 		break;
3987*7c478bd9Sstevel@tonic-gate 	}
3988*7c478bd9Sstevel@tonic-gate 
3989*7c478bd9Sstevel@tonic-gate 	case TELOPT_XDISPLOC: {
3990*7c478bd9Sstevel@tonic-gate 		if (SB_EOF() || SB_GET() != TELQUAL_IS) {
3991*7c478bd9Sstevel@tonic-gate 			return;
3992*7c478bd9Sstevel@tonic-gate 		}
3993*7c478bd9Sstevel@tonic-gate 		settimer(xdisplocsubopt);
3994*7c478bd9Sstevel@tonic-gate 		subpointer[SB_LEN()] = '\0';
3995*7c478bd9Sstevel@tonic-gate 		if ((new_env("DISPLAY", subpointer)) == 1)
3996*7c478bd9Sstevel@tonic-gate 			perror("malloc");
3997*7c478bd9Sstevel@tonic-gate 		break;
3998*7c478bd9Sstevel@tonic-gate 	}
3999*7c478bd9Sstevel@tonic-gate 
4000*7c478bd9Sstevel@tonic-gate 	case TELOPT_NEW_ENVIRON:
4001*7c478bd9Sstevel@tonic-gate 	case TELOPT_OLD_ENVIRON: {
4002*7c478bd9Sstevel@tonic-gate 		int c;
4003*7c478bd9Sstevel@tonic-gate 		char *cp, *varp, *valp;
4004*7c478bd9Sstevel@tonic-gate 
4005*7c478bd9Sstevel@tonic-gate 		if (SB_EOF())
4006*7c478bd9Sstevel@tonic-gate 			return;
4007*7c478bd9Sstevel@tonic-gate 		c = SB_GET();
4008*7c478bd9Sstevel@tonic-gate 		if (c == TELQUAL_IS) {
4009*7c478bd9Sstevel@tonic-gate 			if (subchar == TELOPT_OLD_ENVIRON)
4010*7c478bd9Sstevel@tonic-gate 				settimer(oenvironsubopt);
4011*7c478bd9Sstevel@tonic-gate 			else
4012*7c478bd9Sstevel@tonic-gate 				settimer(environsubopt);
4013*7c478bd9Sstevel@tonic-gate 		} else if (c != TELQUAL_INFO) {
4014*7c478bd9Sstevel@tonic-gate 			return;
4015*7c478bd9Sstevel@tonic-gate 		}
4016*7c478bd9Sstevel@tonic-gate 
4017*7c478bd9Sstevel@tonic-gate 		if (subchar == TELOPT_NEW_ENVIRON) {
4018*7c478bd9Sstevel@tonic-gate 		    while (!SB_EOF()) {
4019*7c478bd9Sstevel@tonic-gate 			c = SB_GET();
4020*7c478bd9Sstevel@tonic-gate 			if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
4021*7c478bd9Sstevel@tonic-gate 				break;
4022*7c478bd9Sstevel@tonic-gate 		    }
4023*7c478bd9Sstevel@tonic-gate 		} else
4024*7c478bd9Sstevel@tonic-gate 		{
4025*7c478bd9Sstevel@tonic-gate 			while (!SB_EOF()) {
4026*7c478bd9Sstevel@tonic-gate 				c = SB_GET();
4027*7c478bd9Sstevel@tonic-gate 				if ((c == env_ovar) || (c == ENV_USERVAR))
4028*7c478bd9Sstevel@tonic-gate 					break;
4029*7c478bd9Sstevel@tonic-gate 			}
4030*7c478bd9Sstevel@tonic-gate 		}
4031*7c478bd9Sstevel@tonic-gate 
4032*7c478bd9Sstevel@tonic-gate 		if (SB_EOF())
4033*7c478bd9Sstevel@tonic-gate 			return;
4034*7c478bd9Sstevel@tonic-gate 
4035*7c478bd9Sstevel@tonic-gate 		cp = varp = (char *)subpointer;
4036*7c478bd9Sstevel@tonic-gate 		valp = 0;
4037*7c478bd9Sstevel@tonic-gate 
4038*7c478bd9Sstevel@tonic-gate 		while (!SB_EOF()) {
4039*7c478bd9Sstevel@tonic-gate 			c = SB_GET();
4040*7c478bd9Sstevel@tonic-gate 			if (subchar == TELOPT_OLD_ENVIRON) {
4041*7c478bd9Sstevel@tonic-gate 				if (c == env_ovar)
4042*7c478bd9Sstevel@tonic-gate 					c = NEW_ENV_VAR;
4043*7c478bd9Sstevel@tonic-gate 				else if (c == env_ovalue)
4044*7c478bd9Sstevel@tonic-gate 					c = NEW_ENV_VALUE;
4045*7c478bd9Sstevel@tonic-gate 			}
4046*7c478bd9Sstevel@tonic-gate 			switch (c) {
4047*7c478bd9Sstevel@tonic-gate 
4048*7c478bd9Sstevel@tonic-gate 			case NEW_ENV_VALUE:
4049*7c478bd9Sstevel@tonic-gate 				*cp = '\0';
4050*7c478bd9Sstevel@tonic-gate 				cp = valp = (char *)subpointer;
4051*7c478bd9Sstevel@tonic-gate 				break;
4052*7c478bd9Sstevel@tonic-gate 
4053*7c478bd9Sstevel@tonic-gate 			case NEW_ENV_VAR:
4054*7c478bd9Sstevel@tonic-gate 			case ENV_USERVAR:
4055*7c478bd9Sstevel@tonic-gate 				*cp = '\0';
4056*7c478bd9Sstevel@tonic-gate 				if (valp) {
4057*7c478bd9Sstevel@tonic-gate 					if ((new_env(varp, valp)) == 1) {
4058*7c478bd9Sstevel@tonic-gate 						perror("malloc");
4059*7c478bd9Sstevel@tonic-gate 					}
4060*7c478bd9Sstevel@tonic-gate 				} else {
4061*7c478bd9Sstevel@tonic-gate 					(void) del_env(varp);
4062*7c478bd9Sstevel@tonic-gate 				}
4063*7c478bd9Sstevel@tonic-gate 				cp = varp = (char *)subpointer;
4064*7c478bd9Sstevel@tonic-gate 				valp = 0;
4065*7c478bd9Sstevel@tonic-gate 				break;
4066*7c478bd9Sstevel@tonic-gate 
4067*7c478bd9Sstevel@tonic-gate 			case ENV_ESC:
4068*7c478bd9Sstevel@tonic-gate 				if (SB_EOF())
4069*7c478bd9Sstevel@tonic-gate 					break;
4070*7c478bd9Sstevel@tonic-gate 				c = SB_GET();
4071*7c478bd9Sstevel@tonic-gate 				/* FALL THROUGH */
4072*7c478bd9Sstevel@tonic-gate 			default:
4073*7c478bd9Sstevel@tonic-gate 				*cp++ = c;
4074*7c478bd9Sstevel@tonic-gate 				break;
4075*7c478bd9Sstevel@tonic-gate 			}
4076*7c478bd9Sstevel@tonic-gate 		}
4077*7c478bd9Sstevel@tonic-gate 		*cp = '\0';
4078*7c478bd9Sstevel@tonic-gate 		if (valp) {
4079*7c478bd9Sstevel@tonic-gate 			if ((new_env(varp, valp)) == 1) {
4080*7c478bd9Sstevel@tonic-gate 				perror("malloc");
4081*7c478bd9Sstevel@tonic-gate 			}
4082*7c478bd9Sstevel@tonic-gate 		} else {
4083*7c478bd9Sstevel@tonic-gate 			(void) del_env(varp);
4084*7c478bd9Sstevel@tonic-gate 		}
4085*7c478bd9Sstevel@tonic-gate 		break;
4086*7c478bd9Sstevel@tonic-gate 	}  /* end of case TELOPT_NEW_ENVIRON */
4087*7c478bd9Sstevel@tonic-gate 
4088*7c478bd9Sstevel@tonic-gate 	case TELOPT_AUTHENTICATION:
4089*7c478bd9Sstevel@tonic-gate 		if (SB_EOF())
4090*7c478bd9Sstevel@tonic-gate 			break;
4091*7c478bd9Sstevel@tonic-gate 		switch (SB_GET()) {
4092*7c478bd9Sstevel@tonic-gate 		case TELQUAL_SEND:
4093*7c478bd9Sstevel@tonic-gate 		case TELQUAL_REPLY:
4094*7c478bd9Sstevel@tonic-gate 			/*
4095*7c478bd9Sstevel@tonic-gate 			 * These are sent server only and cannot be sent by the
4096*7c478bd9Sstevel@tonic-gate 			 * client.
4097*7c478bd9Sstevel@tonic-gate 			 */
4098*7c478bd9Sstevel@tonic-gate 			break;
4099*7c478bd9Sstevel@tonic-gate 		case TELQUAL_IS:
4100*7c478bd9Sstevel@tonic-gate 			if (auth_debug)
4101*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
4102*7c478bd9Sstevel@tonic-gate 					    "RCVD AUTHENTICATION IS "
4103*7c478bd9Sstevel@tonic-gate 					    "(%d bytes)\n",
4104*7c478bd9Sstevel@tonic-gate 					    SB_LEN());
4105*7c478bd9Sstevel@tonic-gate 			if (!auth_negotiated)
4106*7c478bd9Sstevel@tonic-gate 				auth_is((uchar_t *)subpointer, SB_LEN());
4107*7c478bd9Sstevel@tonic-gate 			break;
4108*7c478bd9Sstevel@tonic-gate 		case TELQUAL_NAME:
4109*7c478bd9Sstevel@tonic-gate 			if (auth_debug)
4110*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
4111*7c478bd9Sstevel@tonic-gate 					    "RCVD AUTHENTICATION NAME "
4112*7c478bd9Sstevel@tonic-gate 					    "(%d bytes)\n",
4113*7c478bd9Sstevel@tonic-gate 					    SB_LEN());
4114*7c478bd9Sstevel@tonic-gate 			if (!auth_negotiated)
4115*7c478bd9Sstevel@tonic-gate 				auth_name((uchar_t *)subpointer, SB_LEN());
4116*7c478bd9Sstevel@tonic-gate 			break;
4117*7c478bd9Sstevel@tonic-gate 		}
4118*7c478bd9Sstevel@tonic-gate 		break;
4119*7c478bd9Sstevel@tonic-gate 
4120*7c478bd9Sstevel@tonic-gate 	case TELOPT_ENCRYPT: {
4121*7c478bd9Sstevel@tonic-gate 		int c;
4122*7c478bd9Sstevel@tonic-gate 		if (SB_EOF())
4123*7c478bd9Sstevel@tonic-gate 			break;
4124*7c478bd9Sstevel@tonic-gate 		c = SB_GET();
4125*7c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
4126*7c478bd9Sstevel@tonic-gate 		if (enc_debug)
4127*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "RCVD ENCRYPT %s\n",
4128*7c478bd9Sstevel@tonic-gate 				    ENCRYPT_NAME(c));
4129*7c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
4130*7c478bd9Sstevel@tonic-gate 		switch (c) {
4131*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_SUPPORT:
4132*7c478bd9Sstevel@tonic-gate 			encrypt_support(subpointer, SB_LEN());
4133*7c478bd9Sstevel@tonic-gate 			break;
4134*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_IS:
4135*7c478bd9Sstevel@tonic-gate 			encrypt_is((uchar_t *)subpointer, SB_LEN());
4136*7c478bd9Sstevel@tonic-gate 			break;
4137*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_REPLY:
4138*7c478bd9Sstevel@tonic-gate 			(void) encrypt_reply(subpointer, SB_LEN());
4139*7c478bd9Sstevel@tonic-gate 			break;
4140*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_START:
4141*7c478bd9Sstevel@tonic-gate 			encrypt_start();
4142*7c478bd9Sstevel@tonic-gate 			break;
4143*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_END:
4144*7c478bd9Sstevel@tonic-gate 			encrypt_end(TELNET_DIR_DECRYPT);
4145*7c478bd9Sstevel@tonic-gate 			break;
4146*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_REQSTART:
4147*7c478bd9Sstevel@tonic-gate 			encrypt_request_start();
4148*7c478bd9Sstevel@tonic-gate 			break;
4149*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_REQEND:
4150*7c478bd9Sstevel@tonic-gate 			/*
4151*7c478bd9Sstevel@tonic-gate 			 * We can always send an REQEND so that we cannot
4152*7c478bd9Sstevel@tonic-gate 			 * get stuck encrypting.  We should only get this
4153*7c478bd9Sstevel@tonic-gate 			 * if we have been able to get in the correct mode
4154*7c478bd9Sstevel@tonic-gate 			 * anyhow.
4155*7c478bd9Sstevel@tonic-gate 			 */
4156*7c478bd9Sstevel@tonic-gate 			encrypt_request_end();
4157*7c478bd9Sstevel@tonic-gate 			break;
4158*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_ENC_KEYID:
4159*7c478bd9Sstevel@tonic-gate 			encrypt_enc_keyid(subpointer, SB_LEN());
4160*7c478bd9Sstevel@tonic-gate 			break;
4161*7c478bd9Sstevel@tonic-gate 		case ENCRYPT_DEC_KEYID:
4162*7c478bd9Sstevel@tonic-gate 			encrypt_dec_keyid(subpointer, SB_LEN());
4163*7c478bd9Sstevel@tonic-gate 			break;
4164*7c478bd9Sstevel@tonic-gate 		default:
4165*7c478bd9Sstevel@tonic-gate 			break;
4166*7c478bd9Sstevel@tonic-gate 		}
4167*7c478bd9Sstevel@tonic-gate 	}
4168*7c478bd9Sstevel@tonic-gate 	break;
4169*7c478bd9Sstevel@tonic-gate 
4170*7c478bd9Sstevel@tonic-gate 	default:
4171*7c478bd9Sstevel@tonic-gate 		break;
4172*7c478bd9Sstevel@tonic-gate 	}
4173*7c478bd9Sstevel@tonic-gate }
4174*7c478bd9Sstevel@tonic-gate 
4175*7c478bd9Sstevel@tonic-gate static void
4176*7c478bd9Sstevel@tonic-gate mode(int on, int off)
4177*7c478bd9Sstevel@tonic-gate {
4178*7c478bd9Sstevel@tonic-gate 	struct termios  tios;
4179*7c478bd9Sstevel@tonic-gate 
4180*7c478bd9Sstevel@tonic-gate 	ptyflush();
4181*7c478bd9Sstevel@tonic-gate 	if (tcgetattr(pty, &tios) < 0)
4182*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "tcgetattr: %m\n");
4183*7c478bd9Sstevel@tonic-gate 
4184*7c478bd9Sstevel@tonic-gate 	if (on & O_RAW) {
4185*7c478bd9Sstevel@tonic-gate 		tios.c_cflag |= CS8;
4186*7c478bd9Sstevel@tonic-gate 		tios.c_iflag &= ~IUCLC;
4187*7c478bd9Sstevel@tonic-gate 		tios.c_lflag &= ~(XCASE|IEXTEN);
4188*7c478bd9Sstevel@tonic-gate 	}
4189*7c478bd9Sstevel@tonic-gate 	if (off & O_RAW) {
4190*7c478bd9Sstevel@tonic-gate 		if ((tios.c_cflag & PARENB) != 0)
4191*7c478bd9Sstevel@tonic-gate 			tios.c_cflag &= ~CS8;
4192*7c478bd9Sstevel@tonic-gate 		tios.c_lflag |= IEXTEN;
4193*7c478bd9Sstevel@tonic-gate 	}
4194*7c478bd9Sstevel@tonic-gate 
4195*7c478bd9Sstevel@tonic-gate 	if (on & O_ECHO)
4196*7c478bd9Sstevel@tonic-gate 		tios.c_lflag |= ECHO;
4197*7c478bd9Sstevel@tonic-gate 	if (off & O_ECHO)
4198*7c478bd9Sstevel@tonic-gate 		tios.c_lflag &= ~ECHO;
4199*7c478bd9Sstevel@tonic-gate 
4200*7c478bd9Sstevel@tonic-gate 	if (on & O_CRMOD) {
4201*7c478bd9Sstevel@tonic-gate 		tios.c_iflag |= ICRNL;
4202*7c478bd9Sstevel@tonic-gate 		tios.c_oflag |= ONLCR;
4203*7c478bd9Sstevel@tonic-gate 	}
4204*7c478bd9Sstevel@tonic-gate 	/*
4205*7c478bd9Sstevel@tonic-gate 	 * Because "O_CRMOD" will never be set in "off" we don't have to
4206*7c478bd9Sstevel@tonic-gate 	 * handle this case here.
4207*7c478bd9Sstevel@tonic-gate 	 */
4208*7c478bd9Sstevel@tonic-gate 
4209*7c478bd9Sstevel@tonic-gate 	if (tcsetattr(pty, TCSANOW, &tios) < 0)
4210*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "tcsetattr: %m\n");
4211*7c478bd9Sstevel@tonic-gate }
4212*7c478bd9Sstevel@tonic-gate 
4213*7c478bd9Sstevel@tonic-gate /*
4214*7c478bd9Sstevel@tonic-gate  * Send interrupt to process on other side of pty.
4215*7c478bd9Sstevel@tonic-gate  * If it is in raw mode, just write NULL;
4216*7c478bd9Sstevel@tonic-gate  * otherwise, write intr char.
4217*7c478bd9Sstevel@tonic-gate  */
4218*7c478bd9Sstevel@tonic-gate static void
4219*7c478bd9Sstevel@tonic-gate interrupt(void)
4220*7c478bd9Sstevel@tonic-gate {
4221*7c478bd9Sstevel@tonic-gate 	struct sgttyb b;
4222*7c478bd9Sstevel@tonic-gate 	struct tchars tchars;
4223*7c478bd9Sstevel@tonic-gate 
4224*7c478bd9Sstevel@tonic-gate 	ptyflush();	/* half-hearted */
4225*7c478bd9Sstevel@tonic-gate 	if (ioctl(pty, TIOCGETP, &b) == -1)
4226*7c478bd9Sstevel@tonic-gate 		syslog(LOG_INFO, "ioctl TIOCGETP: %m\n");
4227*7c478bd9Sstevel@tonic-gate 	if (b.sg_flags & O_RAW) {
4228*7c478bd9Sstevel@tonic-gate 		*pfrontp++ = '\0';
4229*7c478bd9Sstevel@tonic-gate 		return;
4230*7c478bd9Sstevel@tonic-gate 	}
4231*7c478bd9Sstevel@tonic-gate 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
4232*7c478bd9Sstevel@tonic-gate 		'\177' : tchars.t_intrc;
4233*7c478bd9Sstevel@tonic-gate }
4234*7c478bd9Sstevel@tonic-gate 
4235*7c478bd9Sstevel@tonic-gate /*
4236*7c478bd9Sstevel@tonic-gate  * Send quit to process on other side of pty.
4237*7c478bd9Sstevel@tonic-gate  * If it is in raw mode, just write NULL;
4238*7c478bd9Sstevel@tonic-gate  * otherwise, write quit char.
4239*7c478bd9Sstevel@tonic-gate  */
4240*7c478bd9Sstevel@tonic-gate static void
4241*7c478bd9Sstevel@tonic-gate sendbrk(void)
4242*7c478bd9Sstevel@tonic-gate {
4243*7c478bd9Sstevel@tonic-gate 	struct sgttyb b;
4244*7c478bd9Sstevel@tonic-gate 	struct tchars tchars;
4245*7c478bd9Sstevel@tonic-gate 
4246*7c478bd9Sstevel@tonic-gate 	ptyflush();	/* half-hearted */
4247*7c478bd9Sstevel@tonic-gate 	(void) ioctl(pty, TIOCGETP, &b);
4248*7c478bd9Sstevel@tonic-gate 	if (b.sg_flags & O_RAW) {
4249*7c478bd9Sstevel@tonic-gate 		*pfrontp++ = '\0';
4250*7c478bd9Sstevel@tonic-gate 		return;
4251*7c478bd9Sstevel@tonic-gate 	}
4252*7c478bd9Sstevel@tonic-gate 	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
4253*7c478bd9Sstevel@tonic-gate 		'\034' : tchars.t_quitc;
4254*7c478bd9Sstevel@tonic-gate }
4255*7c478bd9Sstevel@tonic-gate 
4256*7c478bd9Sstevel@tonic-gate static void
4257*7c478bd9Sstevel@tonic-gate ptyflush(void)
4258*7c478bd9Sstevel@tonic-gate {
4259*7c478bd9Sstevel@tonic-gate 	int n;
4260*7c478bd9Sstevel@tonic-gate 
4261*7c478bd9Sstevel@tonic-gate 	if ((n = pfrontp - pbackp) > 0)
4262*7c478bd9Sstevel@tonic-gate 		n = write(master, pbackp, n);
4263*7c478bd9Sstevel@tonic-gate 	if (n < 0)
4264*7c478bd9Sstevel@tonic-gate 		return;
4265*7c478bd9Sstevel@tonic-gate 	pbackp += n;
4266*7c478bd9Sstevel@tonic-gate 	if (pbackp == pfrontp)
4267*7c478bd9Sstevel@tonic-gate 		pbackp = pfrontp = ptyobuf;
4268*7c478bd9Sstevel@tonic-gate }
4269*7c478bd9Sstevel@tonic-gate 
4270*7c478bd9Sstevel@tonic-gate /*
4271*7c478bd9Sstevel@tonic-gate  * nextitem()
4272*7c478bd9Sstevel@tonic-gate  *
4273*7c478bd9Sstevel@tonic-gate  *	Return the address of the next "item" in the TELNET data
4274*7c478bd9Sstevel@tonic-gate  * stream.  This will be the address of the next character if
4275*7c478bd9Sstevel@tonic-gate  * the current address is a user data character, or it will
4276*7c478bd9Sstevel@tonic-gate  * be the address of the character following the TELNET command
4277*7c478bd9Sstevel@tonic-gate  * if the current address is a TELNET IAC ("I Am a Command")
4278*7c478bd9Sstevel@tonic-gate  * character.
4279*7c478bd9Sstevel@tonic-gate  */
4280*7c478bd9Sstevel@tonic-gate 
4281*7c478bd9Sstevel@tonic-gate static char *
4282*7c478bd9Sstevel@tonic-gate nextitem(char *current)
4283*7c478bd9Sstevel@tonic-gate {
4284*7c478bd9Sstevel@tonic-gate 	if ((*current&0xff) != IAC) {
4285*7c478bd9Sstevel@tonic-gate 		return (current+1);
4286*7c478bd9Sstevel@tonic-gate 	}
4287*7c478bd9Sstevel@tonic-gate 	switch (*(current+1)&0xff) {
4288*7c478bd9Sstevel@tonic-gate 	case DO:
4289*7c478bd9Sstevel@tonic-gate 	case DONT:
4290*7c478bd9Sstevel@tonic-gate 	case WILL:
4291*7c478bd9Sstevel@tonic-gate 	case WONT:
4292*7c478bd9Sstevel@tonic-gate 		return (current+3);
4293*7c478bd9Sstevel@tonic-gate 	case SB:		/* loop forever looking for the SE */
4294*7c478bd9Sstevel@tonic-gate 	{
4295*7c478bd9Sstevel@tonic-gate 		char *look = current+2;
4296*7c478bd9Sstevel@tonic-gate 
4297*7c478bd9Sstevel@tonic-gate 		for (;;) {
4298*7c478bd9Sstevel@tonic-gate 			if ((*look++&0xff) == IAC) {
4299*7c478bd9Sstevel@tonic-gate 				if ((*look++&0xff) == SE) {
4300*7c478bd9Sstevel@tonic-gate 					return (look);
4301*7c478bd9Sstevel@tonic-gate 				}
4302*7c478bd9Sstevel@tonic-gate 			}
4303*7c478bd9Sstevel@tonic-gate 		}
4304*7c478bd9Sstevel@tonic-gate 	}
4305*7c478bd9Sstevel@tonic-gate 	default:
4306*7c478bd9Sstevel@tonic-gate 		return (current+2);
4307*7c478bd9Sstevel@tonic-gate 	}
4308*7c478bd9Sstevel@tonic-gate }
4309*7c478bd9Sstevel@tonic-gate 
4310*7c478bd9Sstevel@tonic-gate 
4311*7c478bd9Sstevel@tonic-gate /*
4312*7c478bd9Sstevel@tonic-gate  * netclear()
4313*7c478bd9Sstevel@tonic-gate  *
4314*7c478bd9Sstevel@tonic-gate  *	We are about to do a TELNET SYNCH operation.  Clear
4315*7c478bd9Sstevel@tonic-gate  * the path to the network.
4316*7c478bd9Sstevel@tonic-gate  *
4317*7c478bd9Sstevel@tonic-gate  *	Things are a bit tricky since we may have sent the first
4318*7c478bd9Sstevel@tonic-gate  * byte or so of a previous TELNET command into the network.
4319*7c478bd9Sstevel@tonic-gate  * So, we have to scan the network buffer from the beginning
4320*7c478bd9Sstevel@tonic-gate  * until we are up to where we want to be.
4321*7c478bd9Sstevel@tonic-gate  *
4322*7c478bd9Sstevel@tonic-gate  *	A side effect of what we do, just to keep things
4323*7c478bd9Sstevel@tonic-gate  * simple, is to clear the urgent data pointer.  The principal
4324*7c478bd9Sstevel@tonic-gate  * caller should be setting the urgent data pointer AFTER calling
4325*7c478bd9Sstevel@tonic-gate  * us in any case.
4326*7c478bd9Sstevel@tonic-gate  */
4327*7c478bd9Sstevel@tonic-gate static void
4328*7c478bd9Sstevel@tonic-gate netclear(void)
4329*7c478bd9Sstevel@tonic-gate {
4330*7c478bd9Sstevel@tonic-gate 	char *thisitem, *next;
4331*7c478bd9Sstevel@tonic-gate 	char *good;
4332*7c478bd9Sstevel@tonic-gate #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
4333*7c478bd9Sstevel@tonic-gate 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
4334*7c478bd9Sstevel@tonic-gate 
4335*7c478bd9Sstevel@tonic-gate 	thisitem = netobuf;
4336*7c478bd9Sstevel@tonic-gate 
4337*7c478bd9Sstevel@tonic-gate 	while ((next = nextitem(thisitem)) <= nbackp) {
4338*7c478bd9Sstevel@tonic-gate 		thisitem = next;
4339*7c478bd9Sstevel@tonic-gate 	}
4340*7c478bd9Sstevel@tonic-gate 
4341*7c478bd9Sstevel@tonic-gate 	/* Now, thisitem is first before/at boundary. */
4342*7c478bd9Sstevel@tonic-gate 
4343*7c478bd9Sstevel@tonic-gate 	good = netobuf;	/* where the good bytes go */
4344*7c478bd9Sstevel@tonic-gate 
4345*7c478bd9Sstevel@tonic-gate 	while (nfrontp > thisitem) {
4346*7c478bd9Sstevel@tonic-gate 		if (wewant(thisitem)) {
4347*7c478bd9Sstevel@tonic-gate 			int length;
4348*7c478bd9Sstevel@tonic-gate 
4349*7c478bd9Sstevel@tonic-gate 			next = thisitem;
4350*7c478bd9Sstevel@tonic-gate 			do {
4351*7c478bd9Sstevel@tonic-gate 				next = nextitem(next);
4352*7c478bd9Sstevel@tonic-gate 			} while (wewant(next) && (nfrontp > next));
4353*7c478bd9Sstevel@tonic-gate 			length = next-thisitem;
4354*7c478bd9Sstevel@tonic-gate 			(void) memmove(good, thisitem, length);
4355*7c478bd9Sstevel@tonic-gate 			good += length;
4356*7c478bd9Sstevel@tonic-gate 			thisitem = next;
4357*7c478bd9Sstevel@tonic-gate 		} else {
4358*7c478bd9Sstevel@tonic-gate 			thisitem = nextitem(thisitem);
4359*7c478bd9Sstevel@tonic-gate 		}
4360*7c478bd9Sstevel@tonic-gate 	}
4361*7c478bd9Sstevel@tonic-gate 
4362*7c478bd9Sstevel@tonic-gate 	nbackp = netobuf;
4363*7c478bd9Sstevel@tonic-gate 	nfrontp = good;		/* next byte to be sent */
4364*7c478bd9Sstevel@tonic-gate 	neturg = 0;
4365*7c478bd9Sstevel@tonic-gate }
4366*7c478bd9Sstevel@tonic-gate 
4367*7c478bd9Sstevel@tonic-gate 
4368*7c478bd9Sstevel@tonic-gate /*
4369*7c478bd9Sstevel@tonic-gate  *  netflush
4370*7c478bd9Sstevel@tonic-gate  *		Send as much data as possible to the network,
4371*7c478bd9Sstevel@tonic-gate  *	handling requests for urgent data.
4372*7c478bd9Sstevel@tonic-gate  */
4373*7c478bd9Sstevel@tonic-gate static void
4374*7c478bd9Sstevel@tonic-gate netflush(void)
4375*7c478bd9Sstevel@tonic-gate {
4376*7c478bd9Sstevel@tonic-gate 	int n;
4377*7c478bd9Sstevel@tonic-gate 
4378*7c478bd9Sstevel@tonic-gate 	if ((n = nfrontp - nbackp) > 0) {
4379*7c478bd9Sstevel@tonic-gate 		/*
4380*7c478bd9Sstevel@tonic-gate 		 * if no urgent data, or if the other side appears to be an
4381*7c478bd9Sstevel@tonic-gate 		 * old 4.2 client (and thus unable to survive TCP urgent data),
4382*7c478bd9Sstevel@tonic-gate 		 * write the entire buffer in non-OOB mode.
4383*7c478bd9Sstevel@tonic-gate 		 */
4384*7c478bd9Sstevel@tonic-gate 		if ((neturg == 0) || (not42 == 0)) {
4385*7c478bd9Sstevel@tonic-gate 			n = write(net, nbackp, n);	/* normal write */
4386*7c478bd9Sstevel@tonic-gate 		} else {
4387*7c478bd9Sstevel@tonic-gate 			n = neturg - nbackp;
4388*7c478bd9Sstevel@tonic-gate 			/*
4389*7c478bd9Sstevel@tonic-gate 			 * In 4.2 (and 4.3) systems, there is some question
4390*7c478bd9Sstevel@tonic-gate 			 * about what byte in a sendOOB operation is the "OOB"
4391*7c478bd9Sstevel@tonic-gate 			 * data.  To make ourselves compatible, we only send ONE
4392*7c478bd9Sstevel@tonic-gate 			 * byte out of band, the one WE THINK should be OOB
4393*7c478bd9Sstevel@tonic-gate 			 * (though we really have more the TCP philosophy of
4394*7c478bd9Sstevel@tonic-gate 			 * urgent data rather than the Unix philosophy of OOB
4395*7c478bd9Sstevel@tonic-gate 			 * data).
4396*7c478bd9Sstevel@tonic-gate 			 */
4397*7c478bd9Sstevel@tonic-gate 			if (n > 1) {
4398*7c478bd9Sstevel@tonic-gate 				/* send URGENT all by itself */
4399*7c478bd9Sstevel@tonic-gate 				n = write(net, nbackp, n-1);
4400*7c478bd9Sstevel@tonic-gate 			} else {
4401*7c478bd9Sstevel@tonic-gate 				/* URGENT data */
4402*7c478bd9Sstevel@tonic-gate 				n = send_oob(net, nbackp, n);
4403*7c478bd9Sstevel@tonic-gate 			}
4404*7c478bd9Sstevel@tonic-gate 		}
4405*7c478bd9Sstevel@tonic-gate 	}
4406*7c478bd9Sstevel@tonic-gate 	if (n < 0) {
4407*7c478bd9Sstevel@tonic-gate 		if (errno == EWOULDBLOCK)
4408*7c478bd9Sstevel@tonic-gate 			return;
4409*7c478bd9Sstevel@tonic-gate 		/* should blow this guy away... */
4410*7c478bd9Sstevel@tonic-gate 		return;
4411*7c478bd9Sstevel@tonic-gate 	}
4412*7c478bd9Sstevel@tonic-gate 
4413*7c478bd9Sstevel@tonic-gate 	nbackp += n;
4414*7c478bd9Sstevel@tonic-gate 
4415*7c478bd9Sstevel@tonic-gate 	if (nbackp >= neturg) {
4416*7c478bd9Sstevel@tonic-gate 		neturg = 0;
4417*7c478bd9Sstevel@tonic-gate 	}
4418*7c478bd9Sstevel@tonic-gate 	if (nbackp == nfrontp) {
4419*7c478bd9Sstevel@tonic-gate 		nbackp = nfrontp = netobuf;
4420*7c478bd9Sstevel@tonic-gate 	}
4421*7c478bd9Sstevel@tonic-gate }
4422*7c478bd9Sstevel@tonic-gate 
4423*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
4424*7c478bd9Sstevel@tonic-gate static void
4425*7c478bd9Sstevel@tonic-gate cleanup(int signum)
4426*7c478bd9Sstevel@tonic-gate {
4427*7c478bd9Sstevel@tonic-gate 	/*
4428*7c478bd9Sstevel@tonic-gate 	 * If the TEL_IOC_ENABLE ioctl hasn't completed, then we need to
4429*7c478bd9Sstevel@tonic-gate 	 * handle closing differently.  We close "net" first and then
4430*7c478bd9Sstevel@tonic-gate 	 * "master" in that order.  We do close(net) first because
4431*7c478bd9Sstevel@tonic-gate 	 * we have no other way to disconnect forwarding between the network
4432*7c478bd9Sstevel@tonic-gate 	 * and master.  So by issuing the close()'s we ensure that no further
4433*7c478bd9Sstevel@tonic-gate 	 * data rises from TCP.  A more complex fix would be adding proper
4434*7c478bd9Sstevel@tonic-gate 	 * support for throwing a "stop" switch for forwarding data between
4435*7c478bd9Sstevel@tonic-gate 	 * logindmux peers.  It's possible to block in the close of the tty
4436*7c478bd9Sstevel@tonic-gate 	 * while the network still receives data and the telmod module is
4437*7c478bd9Sstevel@tonic-gate 	 * TEL_STOPPED.  A denial-of-service attack generates this case,
4438*7c478bd9Sstevel@tonic-gate 	 * see 4102102.
4439*7c478bd9Sstevel@tonic-gate 	 */
4440*7c478bd9Sstevel@tonic-gate 
4441*7c478bd9Sstevel@tonic-gate 	if (!telmod_init_done) {
4442*7c478bd9Sstevel@tonic-gate 		(void) close(net);
4443*7c478bd9Sstevel@tonic-gate 		(void) close(master);
4444*7c478bd9Sstevel@tonic-gate 	}
4445*7c478bd9Sstevel@tonic-gate 	rmut();
4446*7c478bd9Sstevel@tonic-gate 
4447*7c478bd9Sstevel@tonic-gate 	exit(EXIT_FAILURE);
4448*7c478bd9Sstevel@tonic-gate }
4449*7c478bd9Sstevel@tonic-gate 
4450*7c478bd9Sstevel@tonic-gate static void
4451*7c478bd9Sstevel@tonic-gate rmut(void)
4452*7c478bd9Sstevel@tonic-gate {
4453*7c478bd9Sstevel@tonic-gate 	pam_handle_t    *pamh;
4454*7c478bd9Sstevel@tonic-gate 	struct utmpx *up;
4455*7c478bd9Sstevel@tonic-gate 	char user[sizeof (up->ut_user) + 1];
4456*7c478bd9Sstevel@tonic-gate 	char ttyn[sizeof (up->ut_line) + 1];
4457*7c478bd9Sstevel@tonic-gate 	char rhost[sizeof (up->ut_host) + 1];
4458*7c478bd9Sstevel@tonic-gate 
4459*7c478bd9Sstevel@tonic-gate 	/* while cleaning up don't allow disruption */
4460*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, SIG_IGN);
4461*7c478bd9Sstevel@tonic-gate 
4462*7c478bd9Sstevel@tonic-gate 	setutxent();
4463*7c478bd9Sstevel@tonic-gate 	while (up = getutxent()) {
4464*7c478bd9Sstevel@tonic-gate 		if (up->ut_pid == pid) {
4465*7c478bd9Sstevel@tonic-gate 			if (up->ut_type == DEAD_PROCESS) {
4466*7c478bd9Sstevel@tonic-gate 				/*
4467*7c478bd9Sstevel@tonic-gate 				 * Cleaned up elsewhere.
4468*7c478bd9Sstevel@tonic-gate 				 */
4469*7c478bd9Sstevel@tonic-gate 				break;
4470*7c478bd9Sstevel@tonic-gate 			}
4471*7c478bd9Sstevel@tonic-gate 
4472*7c478bd9Sstevel@tonic-gate 			/*
4473*7c478bd9Sstevel@tonic-gate 			 * call pam_close_session if login changed
4474*7c478bd9Sstevel@tonic-gate 			 * the utmpx user entry from type LOGIN_PROCESS
4475*7c478bd9Sstevel@tonic-gate 			 * to type USER_PROCESS, which happens
4476*7c478bd9Sstevel@tonic-gate 			 * after pam_open_session is called.
4477*7c478bd9Sstevel@tonic-gate 			 */
4478*7c478bd9Sstevel@tonic-gate 			if (up->ut_type == USER_PROCESS) {
4479*7c478bd9Sstevel@tonic-gate 				(void) strlcpy(user, up->ut_user,
4480*7c478bd9Sstevel@tonic-gate 					    sizeof (user));
4481*7c478bd9Sstevel@tonic-gate 				(void) strlcpy(ttyn, up->ut_line,
4482*7c478bd9Sstevel@tonic-gate 					    sizeof (ttyn));
4483*7c478bd9Sstevel@tonic-gate 				(void) strlcpy(rhost, up->ut_host,
4484*7c478bd9Sstevel@tonic-gate 					    sizeof (rhost));
4485*7c478bd9Sstevel@tonic-gate 				if ((pam_start("telnet", user, NULL, &pamh)) ==
4486*7c478bd9Sstevel@tonic-gate 				    PAM_SUCCESS) {
4487*7c478bd9Sstevel@tonic-gate 					(void) pam_set_item(pamh, PAM_TTY,
4488*7c478bd9Sstevel@tonic-gate 							    ttyn);
4489*7c478bd9Sstevel@tonic-gate 					(void) pam_set_item(pamh, PAM_RHOST,
4490*7c478bd9Sstevel@tonic-gate 							    rhost);
4491*7c478bd9Sstevel@tonic-gate 					(void) pam_close_session(pamh, 0);
4492*7c478bd9Sstevel@tonic-gate 					(void) pam_end(pamh, PAM_SUCCESS);
4493*7c478bd9Sstevel@tonic-gate 				}
4494*7c478bd9Sstevel@tonic-gate 			}
4495*7c478bd9Sstevel@tonic-gate 
4496*7c478bd9Sstevel@tonic-gate 			up->ut_type = DEAD_PROCESS;
4497*7c478bd9Sstevel@tonic-gate 			up->ut_exit.e_termination = WTERMSIG(0);
4498*7c478bd9Sstevel@tonic-gate 			up->ut_exit.e_exit = WEXITSTATUS(0);
4499*7c478bd9Sstevel@tonic-gate 			(void) time(&up->ut_tv.tv_sec);
4500*7c478bd9Sstevel@tonic-gate 
4501*7c478bd9Sstevel@tonic-gate 			if (modutx(up) == NULL) {
4502*7c478bd9Sstevel@tonic-gate 				/*
4503*7c478bd9Sstevel@tonic-gate 				 * Since modutx failed we'll
4504*7c478bd9Sstevel@tonic-gate 				 * write out the new entry
4505*7c478bd9Sstevel@tonic-gate 				 * ourselves.
4506*7c478bd9Sstevel@tonic-gate 				 */
4507*7c478bd9Sstevel@tonic-gate 				(void) pututxline(up);
4508*7c478bd9Sstevel@tonic-gate 				updwtmpx("wtmpx", up);
4509*7c478bd9Sstevel@tonic-gate 			}
4510*7c478bd9Sstevel@tonic-gate 			break;
4511*7c478bd9Sstevel@tonic-gate 		}
4512*7c478bd9Sstevel@tonic-gate 	}
4513*7c478bd9Sstevel@tonic-gate 
4514*7c478bd9Sstevel@tonic-gate 	endutxent();
4515*7c478bd9Sstevel@tonic-gate 
4516*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, (void (*)())cleanup);
4517*7c478bd9Sstevel@tonic-gate }
4518*7c478bd9Sstevel@tonic-gate 
4519*7c478bd9Sstevel@tonic-gate static int
4520*7c478bd9Sstevel@tonic-gate readstream(int fd, char *buf, int offset)
4521*7c478bd9Sstevel@tonic-gate {
4522*7c478bd9Sstevel@tonic-gate 	struct strbuf ctlbuf, datbuf;
4523*7c478bd9Sstevel@tonic-gate 	union T_primitives tpi;
4524*7c478bd9Sstevel@tonic-gate 	int	ret = 0;
4525*7c478bd9Sstevel@tonic-gate 	int	flags = 0;
4526*7c478bd9Sstevel@tonic-gate 	int	bytes_avail, count;
4527*7c478bd9Sstevel@tonic-gate 
4528*7c478bd9Sstevel@tonic-gate 	(void) memset((char *)&ctlbuf, 0, sizeof (ctlbuf));
4529*7c478bd9Sstevel@tonic-gate 	(void) memset((char *)&datbuf, 0, sizeof (datbuf));
4530*7c478bd9Sstevel@tonic-gate 
4531*7c478bd9Sstevel@tonic-gate 	ctlbuf.buf = (char *)&tpi;
4532*7c478bd9Sstevel@tonic-gate 	ctlbuf.maxlen = sizeof (tpi);
4533*7c478bd9Sstevel@tonic-gate 
4534*7c478bd9Sstevel@tonic-gate 	if (ioctl(fd, I_NREAD, &bytes_avail) < 0) {
4535*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "I_NREAD returned error %m");
4536*7c478bd9Sstevel@tonic-gate 		return (-1);
4537*7c478bd9Sstevel@tonic-gate 	}
4538*7c478bd9Sstevel@tonic-gate 	if (bytes_avail > netibufsize - offset) {
4539*7c478bd9Sstevel@tonic-gate 		count = netip - netibuf;
4540*7c478bd9Sstevel@tonic-gate 		netibuf = (char *)realloc(netibuf,
4541*7c478bd9Sstevel@tonic-gate 		    (unsigned)netibufsize + bytes_avail);
4542*7c478bd9Sstevel@tonic-gate 		if (netibuf == NULL) {
4543*7c478bd9Sstevel@tonic-gate 			fatal(net, "netibuf realloc failed\n");
4544*7c478bd9Sstevel@tonic-gate 		}
4545*7c478bd9Sstevel@tonic-gate 		netibufsize += bytes_avail;
4546*7c478bd9Sstevel@tonic-gate 		netip = netibuf + count;
4547*7c478bd9Sstevel@tonic-gate 		buf = netibuf;
4548*7c478bd9Sstevel@tonic-gate 	}
4549*7c478bd9Sstevel@tonic-gate 	datbuf.buf = buf + offset;
4550*7c478bd9Sstevel@tonic-gate 	datbuf.maxlen = netibufsize;
4551*7c478bd9Sstevel@tonic-gate 	ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
4552*7c478bd9Sstevel@tonic-gate 	if (ret < 0) {
4553*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "getmsg returned -1, errno %d\n",
4554*7c478bd9Sstevel@tonic-gate 			errno);
4555*7c478bd9Sstevel@tonic-gate 		return (-1);
4556*7c478bd9Sstevel@tonic-gate 	}
4557*7c478bd9Sstevel@tonic-gate 	if (ctlbuf.len <= 0) {
4558*7c478bd9Sstevel@tonic-gate 		return (datbuf.len);
4559*7c478bd9Sstevel@tonic-gate 	}
4560*7c478bd9Sstevel@tonic-gate 
4561*7c478bd9Sstevel@tonic-gate 	if (tpi.type == T_DATA_REQ) {
4562*7c478bd9Sstevel@tonic-gate 		return (0);
4563*7c478bd9Sstevel@tonic-gate 	}
4564*7c478bd9Sstevel@tonic-gate 
4565*7c478bd9Sstevel@tonic-gate 	if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
4566*7c478bd9Sstevel@tonic-gate 		cleanup(0);
4567*7c478bd9Sstevel@tonic-gate 	fatal(fd, "no data or protocol element recognized");
4568*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
4569*7c478bd9Sstevel@tonic-gate }
4570*7c478bd9Sstevel@tonic-gate 
4571*7c478bd9Sstevel@tonic-gate static void
4572*7c478bd9Sstevel@tonic-gate drainstream(int size)
4573*7c478bd9Sstevel@tonic-gate {
4574*7c478bd9Sstevel@tonic-gate 	int	nbytes;
4575*7c478bd9Sstevel@tonic-gate 	int	tsize;
4576*7c478bd9Sstevel@tonic-gate 
4577*7c478bd9Sstevel@tonic-gate 	tsize = netip - netibuf;
4578*7c478bd9Sstevel@tonic-gate 
4579*7c478bd9Sstevel@tonic-gate 	if ((tsize + ncc + size) > netibufsize) {
4580*7c478bd9Sstevel@tonic-gate 		if (!(netibuf = (char *)realloc(netibuf,
4581*7c478bd9Sstevel@tonic-gate 		    (unsigned)tsize + ncc + size)))
4582*7c478bd9Sstevel@tonic-gate 			fatalperror(net, "netibuf realloc failed\n", errno);
4583*7c478bd9Sstevel@tonic-gate 		netibufsize = tsize + ncc + size;
4584*7c478bd9Sstevel@tonic-gate 
4585*7c478bd9Sstevel@tonic-gate 		netip = netibuf + tsize;
4586*7c478bd9Sstevel@tonic-gate 	}
4587*7c478bd9Sstevel@tonic-gate 
4588*7c478bd9Sstevel@tonic-gate 	if ((nbytes = read(net, (char *)netip + ncc, size)) != size)
4589*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, "read %d bytes\n", nbytes);
4590*7c478bd9Sstevel@tonic-gate }
4591*7c478bd9Sstevel@tonic-gate 
4592*7c478bd9Sstevel@tonic-gate /*
4593*7c478bd9Sstevel@tonic-gate  * TPI style replacement for socket send() primitive, so we don't require
4594*7c478bd9Sstevel@tonic-gate  * sockmod to be on the stream.
4595*7c478bd9Sstevel@tonic-gate  */
4596*7c478bd9Sstevel@tonic-gate static int
4597*7c478bd9Sstevel@tonic-gate send_oob(int fd, char *ptr, int count)
4598*7c478bd9Sstevel@tonic-gate {
4599*7c478bd9Sstevel@tonic-gate 	struct T_exdata_req exd_req;
4600*7c478bd9Sstevel@tonic-gate 	struct strbuf hdr, dat;
4601*7c478bd9Sstevel@tonic-gate 	int ret;
4602*7c478bd9Sstevel@tonic-gate 
4603*7c478bd9Sstevel@tonic-gate 	exd_req.PRIM_type = T_EXDATA_REQ;
4604*7c478bd9Sstevel@tonic-gate 	exd_req.MORE_flag = 0;
4605*7c478bd9Sstevel@tonic-gate 
4606*7c478bd9Sstevel@tonic-gate 	hdr.buf = (char *)&exd_req;
4607*7c478bd9Sstevel@tonic-gate 	hdr.len = sizeof (exd_req);
4608*7c478bd9Sstevel@tonic-gate 
4609*7c478bd9Sstevel@tonic-gate 	dat.buf = ptr;
4610*7c478bd9Sstevel@tonic-gate 	dat.len = count;
4611*7c478bd9Sstevel@tonic-gate 
4612*7c478bd9Sstevel@tonic-gate 	ret = putmsg(fd, &hdr, &dat, 0);
4613*7c478bd9Sstevel@tonic-gate 	if (ret == 0) {
4614*7c478bd9Sstevel@tonic-gate 		ret = count;
4615*7c478bd9Sstevel@tonic-gate 	}
4616*7c478bd9Sstevel@tonic-gate 	return (ret);
4617*7c478bd9Sstevel@tonic-gate }
4618*7c478bd9Sstevel@tonic-gate 
4619*7c478bd9Sstevel@tonic-gate 
4620*7c478bd9Sstevel@tonic-gate /*
4621*7c478bd9Sstevel@tonic-gate  * local_setenv --
4622*7c478bd9Sstevel@tonic-gate  *	Set the value of the environmental variable "name" to be
4623*7c478bd9Sstevel@tonic-gate  *	"value".  If rewrite is set, replace any current value.
4624*7c478bd9Sstevel@tonic-gate  */
4625*7c478bd9Sstevel@tonic-gate static int
4626*7c478bd9Sstevel@tonic-gate local_setenv(const char *name, const char *value, int rewrite)
4627*7c478bd9Sstevel@tonic-gate {
4628*7c478bd9Sstevel@tonic-gate 	static int alloced;			/* if allocated space before */
4629*7c478bd9Sstevel@tonic-gate 	char *c;
4630*7c478bd9Sstevel@tonic-gate 	int l_value, offset;
4631*7c478bd9Sstevel@tonic-gate 
4632*7c478bd9Sstevel@tonic-gate 	/*
4633*7c478bd9Sstevel@tonic-gate 	 * Do not allow environment variables which begin with LD_ to be
4634*7c478bd9Sstevel@tonic-gate 	 * inserted into the environment.  While normally the dynamic linker
4635*7c478bd9Sstevel@tonic-gate 	 * protects the login program, that is based on the assumption hostile
4636*7c478bd9Sstevel@tonic-gate 	 * invocation of login are from non-root users.  However, since telnetd
4637*7c478bd9Sstevel@tonic-gate 	 * runs as root, this cannot be utilized.  So instead we simply
4638*7c478bd9Sstevel@tonic-gate 	 * prevent LD_* from being inserted into the environment.
4639*7c478bd9Sstevel@tonic-gate 	 * This also applies to other environment variables that
4640*7c478bd9Sstevel@tonic-gate 	 * are to be ignored in setugid apps.
4641*7c478bd9Sstevel@tonic-gate 	 * Note that at this point name can contain '='!
4642*7c478bd9Sstevel@tonic-gate 	 * Also, do not allow TTYPROMPT to be passed along here.
4643*7c478bd9Sstevel@tonic-gate 	 */
4644*7c478bd9Sstevel@tonic-gate 	if (strncmp(name, "LD_", 3) == 0 ||
4645*7c478bd9Sstevel@tonic-gate 	    strncmp(name, "NLSPATH", 7) == 0 ||
4646*7c478bd9Sstevel@tonic-gate 	    (strncmp(name, "TTYPROMPT", 9) == 0 &&
4647*7c478bd9Sstevel@tonic-gate 		(name[9] == '\0' || name[9] == '='))) {
4648*7c478bd9Sstevel@tonic-gate 		return (-1);
4649*7c478bd9Sstevel@tonic-gate 	}
4650*7c478bd9Sstevel@tonic-gate 	if (*value == '=')			/* no `=' in value */
4651*7c478bd9Sstevel@tonic-gate 		++value;
4652*7c478bd9Sstevel@tonic-gate 	l_value = strlen(value);
4653*7c478bd9Sstevel@tonic-gate 	if ((c = __findenv(name, &offset))) {	/* find if already exists */
4654*7c478bd9Sstevel@tonic-gate 		if (!rewrite)
4655*7c478bd9Sstevel@tonic-gate 			return (0);
4656*7c478bd9Sstevel@tonic-gate 		if ((int)strlen(c) >= l_value) { /* old larger; copy over */
4657*7c478bd9Sstevel@tonic-gate 			while (*c++ = *value++);
4658*7c478bd9Sstevel@tonic-gate 			return (0);
4659*7c478bd9Sstevel@tonic-gate 		}
4660*7c478bd9Sstevel@tonic-gate 	} else {					/* create new slot */
4661*7c478bd9Sstevel@tonic-gate 		int cnt;
4662*7c478bd9Sstevel@tonic-gate 		char **p;
4663*7c478bd9Sstevel@tonic-gate 
4664*7c478bd9Sstevel@tonic-gate 		for (p = environ, cnt = 0; *p; ++p, ++cnt);
4665*7c478bd9Sstevel@tonic-gate 		if (alloced) {			/* just increase size */
4666*7c478bd9Sstevel@tonic-gate 			environ = (char **)realloc((char *)environ,
4667*7c478bd9Sstevel@tonic-gate 			    (size_t)(sizeof (char *) * (cnt + 2)));
4668*7c478bd9Sstevel@tonic-gate 			if (!environ)
4669*7c478bd9Sstevel@tonic-gate 				return (-1);
4670*7c478bd9Sstevel@tonic-gate 		} else {				/* get new space */
4671*7c478bd9Sstevel@tonic-gate 			alloced = 1;		/* copy old entries into it */
4672*7c478bd9Sstevel@tonic-gate 			p = (char **)malloc((size_t)(sizeof (char *)*
4673*7c478bd9Sstevel@tonic-gate 			    (cnt + 2)));
4674*7c478bd9Sstevel@tonic-gate 			if (!p)
4675*7c478bd9Sstevel@tonic-gate 				return (-1);
4676*7c478bd9Sstevel@tonic-gate 			(void) memcpy(p, environ, cnt * sizeof (char *));
4677*7c478bd9Sstevel@tonic-gate 			environ = p;
4678*7c478bd9Sstevel@tonic-gate 		}
4679*7c478bd9Sstevel@tonic-gate 		environ[cnt + 1] = NULL;
4680*7c478bd9Sstevel@tonic-gate 		offset = cnt;
4681*7c478bd9Sstevel@tonic-gate 	}
4682*7c478bd9Sstevel@tonic-gate 	for (c = (char *)name; *c && *c != '='; ++c);	/* no `=' in name */
4683*7c478bd9Sstevel@tonic-gate 	if (!(environ[offset] =			/* name + `=' + value */
4684*7c478bd9Sstevel@tonic-gate 	    malloc((size_t)((int)(c - name) + l_value + 2))))
4685*7c478bd9Sstevel@tonic-gate 		return (-1);
4686*7c478bd9Sstevel@tonic-gate 	for (c = environ[offset]; ((*c = *name++) != 0) && (*c != '='); ++c);
4687*7c478bd9Sstevel@tonic-gate 	for (*c++ = '='; *c++ = *value++; );
4688*7c478bd9Sstevel@tonic-gate 	return (0);
4689*7c478bd9Sstevel@tonic-gate }
4690*7c478bd9Sstevel@tonic-gate 
4691*7c478bd9Sstevel@tonic-gate /*
4692*7c478bd9Sstevel@tonic-gate  * local_unsetenv(name) --
4693*7c478bd9Sstevel@tonic-gate  *	Delete environmental variable "name".
4694*7c478bd9Sstevel@tonic-gate  */
4695*7c478bd9Sstevel@tonic-gate static void
4696*7c478bd9Sstevel@tonic-gate local_unsetenv(const char *name)
4697*7c478bd9Sstevel@tonic-gate {
4698*7c478bd9Sstevel@tonic-gate 	char **p;
4699*7c478bd9Sstevel@tonic-gate 	int offset;
4700*7c478bd9Sstevel@tonic-gate 
4701*7c478bd9Sstevel@tonic-gate 	while (__findenv(name, &offset))	/* if set multiple times */
4702*7c478bd9Sstevel@tonic-gate 		for (p = &environ[offset]; ; ++p)
4703*7c478bd9Sstevel@tonic-gate 			if ((*p = *(p + 1)) == 0)
4704*7c478bd9Sstevel@tonic-gate 				break;
4705*7c478bd9Sstevel@tonic-gate }
4706*7c478bd9Sstevel@tonic-gate 
4707*7c478bd9Sstevel@tonic-gate /*
4708*7c478bd9Sstevel@tonic-gate  * __findenv --
4709*7c478bd9Sstevel@tonic-gate  *	Returns pointer to value associated with name, if any, else NULL.
4710*7c478bd9Sstevel@tonic-gate  *	Sets offset to be the offset of the name/value combination in the
4711*7c478bd9Sstevel@tonic-gate  *	environmental array, for use by local_setenv() and local_unsetenv().
4712*7c478bd9Sstevel@tonic-gate  *	Explicitly removes '=' in argument name.
4713*7c478bd9Sstevel@tonic-gate  */
4714*7c478bd9Sstevel@tonic-gate static char *
4715*7c478bd9Sstevel@tonic-gate __findenv(const char *name, int *offset)
4716*7c478bd9Sstevel@tonic-gate {
4717*7c478bd9Sstevel@tonic-gate 	extern char **environ;
4718*7c478bd9Sstevel@tonic-gate 	int len;
4719*7c478bd9Sstevel@tonic-gate 	const char *np;
4720*7c478bd9Sstevel@tonic-gate 	char **p, *c;
4721*7c478bd9Sstevel@tonic-gate 
4722*7c478bd9Sstevel@tonic-gate 	if (name == NULL || environ == NULL)
4723*7c478bd9Sstevel@tonic-gate 		return (NULL);
4724*7c478bd9Sstevel@tonic-gate 	for (np = name; *np && *np != '='; ++np)
4725*7c478bd9Sstevel@tonic-gate 		continue;
4726*7c478bd9Sstevel@tonic-gate 	len = np - name;
4727*7c478bd9Sstevel@tonic-gate 	for (p = environ; (c = *p) != NULL; ++p)
4728*7c478bd9Sstevel@tonic-gate 		if (strncmp(c, name, len) == 0 && c[len] == '=') {
4729*7c478bd9Sstevel@tonic-gate 			*offset = p - environ;
4730*7c478bd9Sstevel@tonic-gate 			return (c + len + 1);
4731*7c478bd9Sstevel@tonic-gate 		}
4732*7c478bd9Sstevel@tonic-gate 	return (NULL);
4733*7c478bd9Sstevel@tonic-gate }
4734*7c478bd9Sstevel@tonic-gate 
4735*7c478bd9Sstevel@tonic-gate static void
4736*7c478bd9Sstevel@tonic-gate showbanner(void)
4737*7c478bd9Sstevel@tonic-gate {
4738*7c478bd9Sstevel@tonic-gate 	char	*cp;
4739*7c478bd9Sstevel@tonic-gate 	char	evalbuf[BUFSIZ];
4740*7c478bd9Sstevel@tonic-gate 
4741*7c478bd9Sstevel@tonic-gate 	if (defopen(defaultfile) == 0) {
4742*7c478bd9Sstevel@tonic-gate 		int	flags;
4743*7c478bd9Sstevel@tonic-gate 
4744*7c478bd9Sstevel@tonic-gate 		/* ignore case */
4745*7c478bd9Sstevel@tonic-gate 		flags = defcntl(DC_GETFLAGS, 0);
4746*7c478bd9Sstevel@tonic-gate 		TURNOFF(flags, DC_CASE);
4747*7c478bd9Sstevel@tonic-gate 		defcntl(DC_SETFLAGS, flags);
4748*7c478bd9Sstevel@tonic-gate 		if (cp = defread(bannervar)) {
4749*7c478bd9Sstevel@tonic-gate 			FILE	*fp;
4750*7c478bd9Sstevel@tonic-gate 
4751*7c478bd9Sstevel@tonic-gate 			if (strlen(cp) + strlen("eval echo '") + strlen("'\n")
4752*7c478bd9Sstevel@tonic-gate 			    + 1 < sizeof (evalbuf)) {
4753*7c478bd9Sstevel@tonic-gate 				(void) strlcpy(evalbuf, "eval echo '",
4754*7c478bd9Sstevel@tonic-gate 					sizeof (evalbuf));
4755*7c478bd9Sstevel@tonic-gate 				(void) strlcat(evalbuf, cp, sizeof (evalbuf));
4756*7c478bd9Sstevel@tonic-gate 				(void) strlcat(evalbuf, "'\n",
4757*7c478bd9Sstevel@tonic-gate 						sizeof (evalbuf));
4758*7c478bd9Sstevel@tonic-gate 
4759*7c478bd9Sstevel@tonic-gate 				if (fp = popen(evalbuf, "r")) {
4760*7c478bd9Sstevel@tonic-gate 					char	buf[BUFSIZ];
4761*7c478bd9Sstevel@tonic-gate 					size_t	size;
4762*7c478bd9Sstevel@tonic-gate 
4763*7c478bd9Sstevel@tonic-gate 					/*
4764*7c478bd9Sstevel@tonic-gate 					 * Pipe I/O atomicity guarantees we
4765*7c478bd9Sstevel@tonic-gate 					 * need only one read.
4766*7c478bd9Sstevel@tonic-gate 					 */
4767*7c478bd9Sstevel@tonic-gate 					if ((size = fread(buf, 1,
4768*7c478bd9Sstevel@tonic-gate 							sizeof (buf) - 1,
4769*7c478bd9Sstevel@tonic-gate 							fp)) != 0) {
4770*7c478bd9Sstevel@tonic-gate 						char	*p;
4771*7c478bd9Sstevel@tonic-gate 						buf[size] = '\0';
4772*7c478bd9Sstevel@tonic-gate 						p = strrchr(buf, '\n');
4773*7c478bd9Sstevel@tonic-gate 						if (p != NULL)
4774*7c478bd9Sstevel@tonic-gate 							*p = '\0';
4775*7c478bd9Sstevel@tonic-gate 						if (strlen(buf)) {
4776*7c478bd9Sstevel@tonic-gate 							map_banner(buf);
4777*7c478bd9Sstevel@tonic-gate 							netflush();
4778*7c478bd9Sstevel@tonic-gate 						}
4779*7c478bd9Sstevel@tonic-gate 					}
4780*7c478bd9Sstevel@tonic-gate 					(void) pclose(fp);
4781*7c478bd9Sstevel@tonic-gate 					/* close default file */
4782*7c478bd9Sstevel@tonic-gate 					(void) defopen(NULL);
4783*7c478bd9Sstevel@tonic-gate 					return;
4784*7c478bd9Sstevel@tonic-gate 				}
4785*7c478bd9Sstevel@tonic-gate 			}
4786*7c478bd9Sstevel@tonic-gate 		}
4787*7c478bd9Sstevel@tonic-gate 		(void) defopen(NULL);	/* close default file */
4788*7c478bd9Sstevel@tonic-gate 	}
4789*7c478bd9Sstevel@tonic-gate 
4790*7c478bd9Sstevel@tonic-gate 	defbanner();
4791*7c478bd9Sstevel@tonic-gate 	netflush();
4792*7c478bd9Sstevel@tonic-gate }
4793*7c478bd9Sstevel@tonic-gate 
4794*7c478bd9Sstevel@tonic-gate static void
4795*7c478bd9Sstevel@tonic-gate map_banner(char *p)
4796*7c478bd9Sstevel@tonic-gate {
4797*7c478bd9Sstevel@tonic-gate 	char	*q;
4798*7c478bd9Sstevel@tonic-gate 
4799*7c478bd9Sstevel@tonic-gate 	/*
4800*7c478bd9Sstevel@tonic-gate 	 *	Map the banner:  "\n" -> "\r\n" and "\r" -> "\r\0"
4801*7c478bd9Sstevel@tonic-gate 	 */
4802*7c478bd9Sstevel@tonic-gate 	for (q = nfrontp; p && *p && q < nfrontp + sizeof (netobuf) - 1; )
4803*7c478bd9Sstevel@tonic-gate 		if (*p == '\n') {
4804*7c478bd9Sstevel@tonic-gate 			*q++ = '\r';
4805*7c478bd9Sstevel@tonic-gate 			*q++ = '\n';
4806*7c478bd9Sstevel@tonic-gate 			p++;
4807*7c478bd9Sstevel@tonic-gate 		} else if (*p == '\r') {
4808*7c478bd9Sstevel@tonic-gate 			*q++ = '\r';
4809*7c478bd9Sstevel@tonic-gate 			*q++ = '\0';
4810*7c478bd9Sstevel@tonic-gate 			p++;
4811*7c478bd9Sstevel@tonic-gate 		} else
4812*7c478bd9Sstevel@tonic-gate 			*q++ = *p++;
4813*7c478bd9Sstevel@tonic-gate 
4814*7c478bd9Sstevel@tonic-gate 	nfrontp += q - netobuf;
4815*7c478bd9Sstevel@tonic-gate }
4816*7c478bd9Sstevel@tonic-gate 
4817*7c478bd9Sstevel@tonic-gate /*
4818*7c478bd9Sstevel@tonic-gate  * Show banner that getty never gave.  By default, this is `uname -sr`.
4819*7c478bd9Sstevel@tonic-gate  *
4820*7c478bd9Sstevel@tonic-gate  * The banner includes some null's (for TELNET CR disambiguation),
4821*7c478bd9Sstevel@tonic-gate  * so we have to be somewhat complicated.
4822*7c478bd9Sstevel@tonic-gate  */
4823*7c478bd9Sstevel@tonic-gate static void
4824*7c478bd9Sstevel@tonic-gate defbanner(void)
4825*7c478bd9Sstevel@tonic-gate {
4826*7c478bd9Sstevel@tonic-gate 	struct utsname u;
4827*7c478bd9Sstevel@tonic-gate 
4828*7c478bd9Sstevel@tonic-gate 	/*
4829*7c478bd9Sstevel@tonic-gate 	 * Dont show this if the '-h' option was present
4830*7c478bd9Sstevel@tonic-gate 	 */
4831*7c478bd9Sstevel@tonic-gate 	if (!show_hostinfo)
4832*7c478bd9Sstevel@tonic-gate 		return;
4833*7c478bd9Sstevel@tonic-gate 
4834*7c478bd9Sstevel@tonic-gate 	if (uname(&u) == -1)
4835*7c478bd9Sstevel@tonic-gate 		return;
4836*7c478bd9Sstevel@tonic-gate 
4837*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *) BANNER1, sizeof (BANNER1) - 1);
4838*7c478bd9Sstevel@tonic-gate 	write_data_len(u.sysname, strlen(u.sysname));
4839*7c478bd9Sstevel@tonic-gate 	write_data_len(" ", 1);
4840*7c478bd9Sstevel@tonic-gate 	write_data_len(u.release, strlen(u.release));
4841*7c478bd9Sstevel@tonic-gate 	write_data_len((const char *)BANNER2, sizeof (BANNER2) - 1);
4842*7c478bd9Sstevel@tonic-gate }
4843*7c478bd9Sstevel@tonic-gate 
4844*7c478bd9Sstevel@tonic-gate /*
4845*7c478bd9Sstevel@tonic-gate  * Verify that the named module is at the top of the stream
4846*7c478bd9Sstevel@tonic-gate  * and then pop it off.
4847*7c478bd9Sstevel@tonic-gate  */
4848*7c478bd9Sstevel@tonic-gate static int
4849*7c478bd9Sstevel@tonic-gate removemod(int f, char *modname)
4850*7c478bd9Sstevel@tonic-gate {
4851*7c478bd9Sstevel@tonic-gate 	char topmodname[BUFSIZ];
4852*7c478bd9Sstevel@tonic-gate 
4853*7c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_LOOK, topmodname) < 0)
4854*7c478bd9Sstevel@tonic-gate 		return (-1);
4855*7c478bd9Sstevel@tonic-gate 	if (strcmp(modname, topmodname) != 0) {
4856*7c478bd9Sstevel@tonic-gate 		errno = ENXIO;
4857*7c478bd9Sstevel@tonic-gate 		return (-1);
4858*7c478bd9Sstevel@tonic-gate 	}
4859*7c478bd9Sstevel@tonic-gate 	if (ioctl(f, I_POP, 0) < 0)
4860*7c478bd9Sstevel@tonic-gate 		return (-1);
4861*7c478bd9Sstevel@tonic-gate 	return (0);
4862*7c478bd9Sstevel@tonic-gate }
4863*7c478bd9Sstevel@tonic-gate 
4864*7c478bd9Sstevel@tonic-gate static void
4865*7c478bd9Sstevel@tonic-gate write_data(const char *format, ...)
4866*7c478bd9Sstevel@tonic-gate {
4867*7c478bd9Sstevel@tonic-gate 	va_list args;
4868*7c478bd9Sstevel@tonic-gate 	int		len;
4869*7c478bd9Sstevel@tonic-gate 	char	argp[BUFSIZ];
4870*7c478bd9Sstevel@tonic-gate 
4871*7c478bd9Sstevel@tonic-gate 	va_start(args, format);
4872*7c478bd9Sstevel@tonic-gate 
4873*7c478bd9Sstevel@tonic-gate 	if ((len = vsnprintf(argp, sizeof (argp), format, args)) == -1)
4874*7c478bd9Sstevel@tonic-gate 		return;
4875*7c478bd9Sstevel@tonic-gate 
4876*7c478bd9Sstevel@tonic-gate 	write_data_len(argp, len);
4877*7c478bd9Sstevel@tonic-gate 	va_end(args);
4878*7c478bd9Sstevel@tonic-gate }
4879*7c478bd9Sstevel@tonic-gate 
4880*7c478bd9Sstevel@tonic-gate static void
4881*7c478bd9Sstevel@tonic-gate write_data_len(const char *buf, int len)
4882*7c478bd9Sstevel@tonic-gate {
4883*7c478bd9Sstevel@tonic-gate 	int remaining, copied;
4884*7c478bd9Sstevel@tonic-gate 
4885*7c478bd9Sstevel@tonic-gate 	remaining = BUFSIZ - (nfrontp - netobuf);
4886*7c478bd9Sstevel@tonic-gate 	while (len > 0) {
4887*7c478bd9Sstevel@tonic-gate 		/*
4888*7c478bd9Sstevel@tonic-gate 		 * If there's not enough space in netobuf then
4889*7c478bd9Sstevel@tonic-gate 		 * try to make some.
4890*7c478bd9Sstevel@tonic-gate 		 */
4891*7c478bd9Sstevel@tonic-gate 	if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
4892*7c478bd9Sstevel@tonic-gate 			netflush();
4893*7c478bd9Sstevel@tonic-gate 			remaining = BUFSIZ - (nfrontp - netobuf);
4894*7c478bd9Sstevel@tonic-gate 		}
4895*7c478bd9Sstevel@tonic-gate 		/* Copy as much as we can */
4896*7c478bd9Sstevel@tonic-gate 		copied = remaining > len ? len : remaining;
4897*7c478bd9Sstevel@tonic-gate 		(void) memmove(nfrontp, buf, copied);
4898*7c478bd9Sstevel@tonic-gate 		nfrontp += copied;
4899*7c478bd9Sstevel@tonic-gate 		len -= copied;
4900*7c478bd9Sstevel@tonic-gate 		remaining -= copied;
4901*7c478bd9Sstevel@tonic-gate 		buf += copied;
4902*7c478bd9Sstevel@tonic-gate 	}
4903*7c478bd9Sstevel@tonic-gate }
4904