xref: /dragonfly/libexec/telnetd/sys_term.c (revision 92fc8b5c)
1  /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)sys_term.c	8.4+1 (Berkeley) 5/30/95
34  * $FreeBSD: src/crypto/telnet/telnetd/sys_term.c,v 1.7.2.5 2002/06/17 02:48:02 jmallett Exp $
35  * $DragonFly: src/crypto/telnet/telnetd/sys_term.c,v 1.2 2003/06/17 04:24:37 dillon Exp $
36  */
37 
38 #include <sys/types.h>
39 #include <sys/tty.h>
40 #include <libutil.h>
41 #include <stdlib.h>
42 #include <utmp.h>
43 
44 #include "telnetd.h"
45 #include "pathnames.h"
46 
47 #ifdef	AUTHENTICATION
48 #include <libtelnet/auth.h>
49 #endif
50 
51 int cleanopen(char *);
52 void scrub_env(void);
53 
54 struct	utmp wtmp;
55 
56 #ifdef _PATH_WTMP
57 char    wtmpf[] = _PATH_WTMP;
58 #else
59 char	wtmpf[]	= "/var/log/wtmp";
60 #endif
61 #ifdef _PATH_UTMP
62 char    utmpf[] = _PATH_UTMP;
63 #else
64 char	utmpf[] = "/var/run/utmp";
65 #endif
66 
67 char	*envinit[3];
68 extern char **environ;
69 
70 #define SCPYN(a, b)	(void) strncpy(a, b, sizeof(a))
71 #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
72 
73 #ifdef	t_erase
74 #undef	t_erase
75 #undef	t_kill
76 #undef	t_intrc
77 #undef	t_quitc
78 #undef	t_startc
79 #undef	t_stopc
80 #undef	t_eofc
81 #undef	t_brkc
82 #undef	t_suspc
83 #undef	t_dsuspc
84 #undef	t_rprntc
85 #undef	t_flushc
86 #undef	t_werasc
87 #undef	t_lnextc
88 #endif
89 
90 #ifndef	USE_TERMIO
91 struct termbuf {
92 	struct sgttyb sg;
93 	struct tchars tc;
94 	struct ltchars ltc;
95 	int state;
96 	int lflags;
97 } termbuf, termbuf2;
98 # define	cfsetospeed(tp, val)	(tp)->sg.sg_ospeed = (val)
99 # define	cfsetispeed(tp, val)	(tp)->sg.sg_ispeed = (val)
100 # define	cfgetospeed(tp)		(tp)->sg.sg_ospeed
101 # define	cfgetispeed(tp)		(tp)->sg.sg_ispeed
102 #else	/* USE_TERMIO */
103 # ifndef	TCSANOW
104 #  ifdef TCSETS
105 #   define	TCSANOW		TCSETS
106 #   define	TCSADRAIN	TCSETSW
107 #   define	tcgetattr(f, t)	ioctl(f, TCGETS, (char *)t)
108 #  else
109 #   ifdef TCSETA
110 #    define	TCSANOW		TCSETA
111 #    define	TCSADRAIN	TCSETAW
112 #    define	tcgetattr(f, t)	ioctl(f, TCGETA, (char *)t)
113 #   else
114 #    define	TCSANOW		TIOCSETA
115 #    define	TCSADRAIN	TIOCSETAW
116 #    define	tcgetattr(f, t)	ioctl(f, TIOCGETA, (char *)t)
117 #   endif
118 #  endif
119 #  define	tcsetattr(f, a, t)	ioctl(f, a, t)
120 #  define	cfsetospeed(tp, val)	(tp)->c_cflag &= ~CBAUD; \
121 					(tp)->c_cflag |= (val)
122 #  define	cfgetospeed(tp)		((tp)->c_cflag & CBAUD)
123 #  ifdef CIBAUD
124 #   define	cfsetispeed(tp, val)	(tp)->c_cflag &= ~CIBAUD; \
125 					(tp)->c_cflag |= ((val)<<IBSHIFT)
126 #   define	cfgetispeed(tp)		(((tp)->c_cflag & CIBAUD)>>IBSHIFT)
127 #  else
128 #   define	cfsetispeed(tp, val)	(tp)->c_cflag &= ~CBAUD; \
129 					(tp)->c_cflag |= (val)
130 #   define	cfgetispeed(tp)		((tp)->c_cflag & CBAUD)
131 #  endif
132 # endif /* TCSANOW */
133 struct termios termbuf, termbuf2;	/* pty control structure */
134 #endif	/* USE_TERMIO */
135 
136 #include <sys/types.h>
137 #include <libutil.h>
138 
139 int cleanopen(char *);
140 void scrub_env(void);
141 static char **addarg(char **, const char *);
142 
143 /*
144  * init_termbuf()
145  * copy_termbuf(cp)
146  * set_termbuf()
147  *
148  * These three routines are used to get and set the "termbuf" structure
149  * to and from the kernel.  init_termbuf() gets the current settings.
150  * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
151  * set_termbuf() writes the structure into the kernel.
152  */
153 
154 void
155 init_termbuf(void)
156 {
157 #ifndef	USE_TERMIO
158 	(void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg);
159 	(void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc);
160 	(void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc);
161 # ifdef	TIOCGSTATE
162 	(void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state);
163 # endif
164 #else
165 	(void) tcgetattr(pty, &termbuf);
166 #endif
167 	termbuf2 = termbuf;
168 }
169 
170 #if	defined(LINEMODE) && defined(TIOCPKT_IOCTL)
171 void
172 copy_termbuf(char *cp, size_t len)
173 {
174 	if (len > sizeof(termbuf))
175 		len = sizeof(termbuf);
176 	memmove((char *)&termbuf, cp, len);
177 	termbuf2 = termbuf;
178 }
179 #endif	/* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
180 
181 void
182 set_termbuf(void)
183 {
184 	/*
185 	 * Only make the necessary changes.
186 	 */
187 #ifndef	USE_TERMIO
188 	if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg,
189 							sizeof(termbuf.sg)))
190 		(void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg);
191 	if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc,
192 							sizeof(termbuf.tc)))
193 		(void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc);
194 	if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
195 							sizeof(termbuf.ltc)))
196 		(void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc);
197 	if (termbuf.lflags != termbuf2.lflags)
198 		(void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags);
199 #else	/* USE_TERMIO */
200 	if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
201 		(void) tcsetattr(pty, TCSANOW, &termbuf);
202 #endif	/* USE_TERMIO */
203 }
204 
205 
206 /*
207  * spcset(func, valp, valpp)
208  *
209  * This function takes various special characters (func), and
210  * sets *valp to the current value of that character, and
211  * *valpp to point to where in the "termbuf" structure that
212  * value is kept.
213  *
214  * It returns the SLC_ level of support for this function.
215  */
216 
217 #ifndef	USE_TERMIO
218 int
219 spcset(int func, cc_t *valp, cc_t **valpp)
220 {
221 	switch(func) {
222 	case SLC_EOF:
223 		*valp = termbuf.tc.t_eofc;
224 		*valpp = (cc_t *)&termbuf.tc.t_eofc;
225 		return(SLC_VARIABLE);
226 	case SLC_EC:
227 		*valp = termbuf.sg.sg_erase;
228 		*valpp = (cc_t *)&termbuf.sg.sg_erase;
229 		return(SLC_VARIABLE);
230 	case SLC_EL:
231 		*valp = termbuf.sg.sg_kill;
232 		*valpp = (cc_t *)&termbuf.sg.sg_kill;
233 		return(SLC_VARIABLE);
234 	case SLC_IP:
235 		*valp = termbuf.tc.t_intrc;
236 		*valpp = (cc_t *)&termbuf.tc.t_intrc;
237 		return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
238 	case SLC_ABORT:
239 		*valp = termbuf.tc.t_quitc;
240 		*valpp = (cc_t *)&termbuf.tc.t_quitc;
241 		return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
242 	case SLC_XON:
243 		*valp = termbuf.tc.t_startc;
244 		*valpp = (cc_t *)&termbuf.tc.t_startc;
245 		return(SLC_VARIABLE);
246 	case SLC_XOFF:
247 		*valp = termbuf.tc.t_stopc;
248 		*valpp = (cc_t *)&termbuf.tc.t_stopc;
249 		return(SLC_VARIABLE);
250 	case SLC_AO:
251 		*valp = termbuf.ltc.t_flushc;
252 		*valpp = (cc_t *)&termbuf.ltc.t_flushc;
253 		return(SLC_VARIABLE);
254 	case SLC_SUSP:
255 		*valp = termbuf.ltc.t_suspc;
256 		*valpp = (cc_t *)&termbuf.ltc.t_suspc;
257 		return(SLC_VARIABLE);
258 	case SLC_EW:
259 		*valp = termbuf.ltc.t_werasc;
260 		*valpp = (cc_t *)&termbuf.ltc.t_werasc;
261 		return(SLC_VARIABLE);
262 	case SLC_RP:
263 		*valp = termbuf.ltc.t_rprntc;
264 		*valpp = (cc_t *)&termbuf.ltc.t_rprntc;
265 		return(SLC_VARIABLE);
266 	case SLC_LNEXT:
267 		*valp = termbuf.ltc.t_lnextc;
268 		*valpp = (cc_t *)&termbuf.ltc.t_lnextc;
269 		return(SLC_VARIABLE);
270 	case SLC_FORW1:
271 		*valp = termbuf.tc.t_brkc;
272 		*valpp = (cc_t *)&termbuf.ltc.t_lnextc;
273 		return(SLC_VARIABLE);
274 	case SLC_BRK:
275 	case SLC_SYNCH:
276 	case SLC_AYT:
277 	case SLC_EOR:
278 		*valp = (cc_t)0;
279 		*valpp = NULL;
280 		return(SLC_DEFAULT);
281 	default:
282 		*valp = (cc_t)0;
283 		*valpp = NULL;
284 		return(SLC_NOSUPPORT);
285 	}
286 }
287 
288 #else	/* USE_TERMIO */
289 
290 
291 #define	setval(a, b)	*valp = termbuf.c_cc[a]; \
292 			*valpp = &termbuf.c_cc[a]; \
293 			return(b);
294 #define	defval(a) *valp = ((cc_t)a); *valpp = NULL; return(SLC_DEFAULT);
295 
296 int
297 spcset(int func, cc_t *valp, cc_t **valpp)
298 {
299 	switch(func) {
300 	case SLC_EOF:
301 		setval(VEOF, SLC_VARIABLE);
302 	case SLC_EC:
303 		setval(VERASE, SLC_VARIABLE);
304 	case SLC_EL:
305 		setval(VKILL, SLC_VARIABLE);
306 	case SLC_IP:
307 		setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
308 	case SLC_ABORT:
309 		setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
310 	case SLC_XON:
311 #ifdef	VSTART
312 		setval(VSTART, SLC_VARIABLE);
313 #else
314 		defval(0x13);
315 #endif
316 	case SLC_XOFF:
317 #ifdef	VSTOP
318 		setval(VSTOP, SLC_VARIABLE);
319 #else
320 		defval(0x11);
321 #endif
322 	case SLC_EW:
323 #ifdef	VWERASE
324 		setval(VWERASE, SLC_VARIABLE);
325 #else
326 		defval(0);
327 #endif
328 	case SLC_RP:
329 #ifdef	VREPRINT
330 		setval(VREPRINT, SLC_VARIABLE);
331 #else
332 		defval(0);
333 #endif
334 	case SLC_LNEXT:
335 #ifdef	VLNEXT
336 		setval(VLNEXT, SLC_VARIABLE);
337 #else
338 		defval(0);
339 #endif
340 	case SLC_AO:
341 #if	!defined(VDISCARD) && defined(VFLUSHO)
342 # define VDISCARD VFLUSHO
343 #endif
344 #ifdef	VDISCARD
345 		setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
346 #else
347 		defval(0);
348 #endif
349 	case SLC_SUSP:
350 #ifdef	VSUSP
351 		setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
352 #else
353 		defval(0);
354 #endif
355 #ifdef	VEOL
356 	case SLC_FORW1:
357 		setval(VEOL, SLC_VARIABLE);
358 #endif
359 #ifdef	VEOL2
360 	case SLC_FORW2:
361 		setval(VEOL2, SLC_VARIABLE);
362 #endif
363 	case SLC_AYT:
364 #ifdef	VSTATUS
365 		setval(VSTATUS, SLC_VARIABLE);
366 #else
367 		defval(0);
368 #endif
369 
370 	case SLC_BRK:
371 	case SLC_SYNCH:
372 	case SLC_EOR:
373 		defval(0);
374 
375 	default:
376 		*valp = 0;
377 		*valpp = 0;
378 		return(SLC_NOSUPPORT);
379 	}
380 }
381 #endif	/* USE_TERMIO */
382 
383 /*
384  * getpty()
385  *
386  * Allocate a pty.  As a side effect, the external character
387  * array "line" contains the name of the slave side.
388  *
389  * Returns the file descriptor of the opened pty.
390  */
391 char alpha[] = "0123456789abcdefghijklmnopqrstuv";
392 char line[16];
393 
394 int
395 getpty(int *ptynum __unused)
396 {
397 	int p;
398 	const char *cp;
399 	char *p1, *p2;
400 	int i;
401 
402 	(void) strcpy(line, _PATH_DEV);
403 	(void) strcat(line, "ptyXX");
404 	p1 = &line[8];
405 	p2 = &line[9];
406 
407 	for (cp = "pqrsPQRS"; *cp; cp++) {
408 		struct stat stb;
409 
410 		*p1 = *cp;
411 		*p2 = '0';
412 		/*
413 		 * This stat() check is just to keep us from
414 		 * looping through all 256 combinations if there
415 		 * aren't that many ptys available.
416 		 */
417 		if (stat(line, &stb) < 0)
418 			break;
419 		for (i = 0; i < 32; i++) {
420 			*p2 = alpha[i];
421 			p = open(line, 2);
422 			if (p > 0) {
423 				line[5] = 't';
424 				chown(line, 0, 0);
425 				chmod(line, 0600);
426 					return(p);
427 			}
428 		}
429 	}
430 	return(-1);
431 }
432 
433 #ifdef	LINEMODE
434 /*
435  * tty_flowmode()	Find out if flow control is enabled or disabled.
436  * tty_linemode()	Find out if linemode (external processing) is enabled.
437  * tty_setlinemod(on)	Turn on/off linemode.
438  * tty_isecho()		Find out if echoing is turned on.
439  * tty_setecho(on)	Enable/disable character echoing.
440  * tty_israw()		Find out if terminal is in RAW mode.
441  * tty_binaryin(on)	Turn on/off BINARY on input.
442  * tty_binaryout(on)	Turn on/off BINARY on output.
443  * tty_isediting()	Find out if line editing is enabled.
444  * tty_istrapsig()	Find out if signal trapping is enabled.
445  * tty_setedit(on)	Turn on/off line editing.
446  * tty_setsig(on)	Turn on/off signal trapping.
447  * tty_issofttab()	Find out if tab expansion is enabled.
448  * tty_setsofttab(on)	Turn on/off soft tab expansion.
449  * tty_islitecho()	Find out if typed control chars are echoed literally
450  * tty_setlitecho()	Turn on/off literal echo of control chars
451  * tty_tspeed(val)	Set transmit speed to val.
452  * tty_rspeed(val)	Set receive speed to val.
453  */
454 
455 
456 int
457 tty_linemode(void)
458 {
459 #ifndef	USE_TERMIO
460 	return(termbuf.state & TS_EXTPROC);
461 #else
462 	return(termbuf.c_lflag & EXTPROC);
463 #endif
464 }
465 
466 void
467 tty_setlinemode(int on)
468 {
469 #ifdef	TIOCEXT
470 	set_termbuf();
471 	(void) ioctl(pty, TIOCEXT, (char *)&on);
472 	init_termbuf();
473 #else	/* !TIOCEXT */
474 # ifdef	EXTPROC
475 	if (on)
476 		termbuf.c_lflag |= EXTPROC;
477 	else
478 		termbuf.c_lflag &= ~EXTPROC;
479 # endif
480 #endif	/* TIOCEXT */
481 }
482 #endif	/* LINEMODE */
483 
484 int
485 tty_isecho(void)
486 {
487 #ifndef USE_TERMIO
488 	return (termbuf.sg.sg_flags & ECHO);
489 #else
490 	return (termbuf.c_lflag & ECHO);
491 #endif
492 }
493 
494 int
495 tty_flowmode(void)
496 {
497 #ifndef USE_TERMIO
498 	return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0);
499 #else
500 	return((termbuf.c_iflag & IXON) ? 1 : 0);
501 #endif
502 }
503 
504 int
505 tty_restartany(void)
506 {
507 #ifndef USE_TERMIO
508 # ifdef	DECCTQ
509 	return((termbuf.lflags & DECCTQ) ? 0 : 1);
510 # else
511 	return(-1);
512 # endif
513 #else
514 	return((termbuf.c_iflag & IXANY) ? 1 : 0);
515 #endif
516 }
517 
518 void
519 tty_setecho(int on)
520 {
521 #ifndef	USE_TERMIO
522 	if (on)
523 		termbuf.sg.sg_flags |= ECHO|CRMOD;
524 	else
525 		termbuf.sg.sg_flags &= ~(ECHO|CRMOD);
526 #else
527 	if (on)
528 		termbuf.c_lflag |= ECHO;
529 	else
530 		termbuf.c_lflag &= ~ECHO;
531 #endif
532 }
533 
534 int
535 tty_israw(void)
536 {
537 #ifndef USE_TERMIO
538 	return(termbuf.sg.sg_flags & RAW);
539 #else
540 	return(!(termbuf.c_lflag & ICANON));
541 #endif
542 }
543 
544 #ifdef	AUTHENTICATION
545 #if	defined(NO_LOGIN_F) && defined(LOGIN_R)
546 int
547 tty_setraw(int on)
548 {
549 #  ifndef USE_TERMIO
550 	if (on)
551 		termbuf.sg.sg_flags |= RAW;
552 	else
553 		termbuf.sg.sg_flags &= ~RAW;
554 #  else
555 	if (on)
556 		termbuf.c_lflag &= ~ICANON;
557 	else
558 		termbuf.c_lflag |= ICANON;
559 #  endif
560 }
561 #endif
562 #endif /* AUTHENTICATION */
563 
564 void
565 tty_binaryin(int on)
566 {
567 #ifndef	USE_TERMIO
568 	if (on)
569 		termbuf.lflags |= LPASS8;
570 	else
571 		termbuf.lflags &= ~LPASS8;
572 #else
573 	if (on) {
574 		termbuf.c_iflag &= ~ISTRIP;
575 	} else {
576 		termbuf.c_iflag |= ISTRIP;
577 	}
578 #endif
579 }
580 
581 void
582 tty_binaryout(int on)
583 {
584 #ifndef	USE_TERMIO
585 	if (on)
586 		termbuf.lflags |= LLITOUT;
587 	else
588 		termbuf.lflags &= ~LLITOUT;
589 #else
590 	if (on) {
591 		termbuf.c_cflag &= ~(CSIZE|PARENB);
592 		termbuf.c_cflag |= CS8;
593 		termbuf.c_oflag &= ~OPOST;
594 	} else {
595 		termbuf.c_cflag &= ~CSIZE;
596 		termbuf.c_cflag |= CS7|PARENB;
597 		termbuf.c_oflag |= OPOST;
598 	}
599 #endif
600 }
601 
602 int
603 tty_isbinaryin(void)
604 {
605 #ifndef	USE_TERMIO
606 	return(termbuf.lflags & LPASS8);
607 #else
608 	return(!(termbuf.c_iflag & ISTRIP));
609 #endif
610 }
611 
612 int
613 tty_isbinaryout(void)
614 {
615 #ifndef	USE_TERMIO
616 	return(termbuf.lflags & LLITOUT);
617 #else
618 	return(!(termbuf.c_oflag&OPOST));
619 #endif
620 }
621 
622 #ifdef	LINEMODE
623 int
624 tty_isediting(void)
625 {
626 #ifndef USE_TERMIO
627 	return(!(termbuf.sg.sg_flags & (CBREAK|RAW)));
628 #else
629 	return(termbuf.c_lflag & ICANON);
630 #endif
631 }
632 
633 int
634 tty_istrapsig(void)
635 {
636 #ifndef USE_TERMIO
637 	return(!(termbuf.sg.sg_flags&RAW));
638 #else
639 	return(termbuf.c_lflag & ISIG);
640 #endif
641 }
642 
643 void
644 tty_setedit(int on)
645 {
646 #ifndef USE_TERMIO
647 	if (on)
648 		termbuf.sg.sg_flags &= ~CBREAK;
649 	else
650 		termbuf.sg.sg_flags |= CBREAK;
651 #else
652 	if (on)
653 		termbuf.c_lflag |= ICANON;
654 	else
655 		termbuf.c_lflag &= ~ICANON;
656 #endif
657 }
658 
659 void
660 tty_setsig(int on)
661 {
662 #ifndef	USE_TERMIO
663 	if (on)
664 		;
665 #else
666 	if (on)
667 		termbuf.c_lflag |= ISIG;
668 	else
669 		termbuf.c_lflag &= ~ISIG;
670 #endif
671 }
672 #endif	/* LINEMODE */
673 
674 int
675 tty_issofttab(void)
676 {
677 #ifndef	USE_TERMIO
678 	return (termbuf.sg.sg_flags & XTABS);
679 #else
680 # ifdef	OXTABS
681 	return (termbuf.c_oflag & OXTABS);
682 # endif
683 # ifdef	TABDLY
684 	return ((termbuf.c_oflag & TABDLY) == TAB3);
685 # endif
686 #endif
687 }
688 
689 void
690 tty_setsofttab(int on)
691 {
692 #ifndef	USE_TERMIO
693 	if (on)
694 		termbuf.sg.sg_flags |= XTABS;
695 	else
696 		termbuf.sg.sg_flags &= ~XTABS;
697 #else
698 	if (on) {
699 # ifdef	OXTABS
700 		termbuf.c_oflag |= OXTABS;
701 # endif
702 # ifdef	TABDLY
703 		termbuf.c_oflag &= ~TABDLY;
704 		termbuf.c_oflag |= TAB3;
705 # endif
706 	} else {
707 # ifdef	OXTABS
708 		termbuf.c_oflag &= ~OXTABS;
709 # endif
710 # ifdef	TABDLY
711 		termbuf.c_oflag &= ~TABDLY;
712 		termbuf.c_oflag |= TAB0;
713 # endif
714 	}
715 #endif
716 }
717 
718 int
719 tty_islitecho(void)
720 {
721 #ifndef	USE_TERMIO
722 	return (!(termbuf.lflags & LCTLECH));
723 #else
724 # ifdef	ECHOCTL
725 	return (!(termbuf.c_lflag & ECHOCTL));
726 # endif
727 # ifdef	TCTLECH
728 	return (!(termbuf.c_lflag & TCTLECH));
729 # endif
730 # if	!defined(ECHOCTL) && !defined(TCTLECH)
731 	return (0);	/* assumes ctl chars are echoed '^x' */
732 # endif
733 #endif
734 }
735 
736 void
737 tty_setlitecho(int on)
738 {
739 #ifndef	USE_TERMIO
740 	if (on)
741 		termbuf.lflags &= ~LCTLECH;
742 	else
743 		termbuf.lflags |= LCTLECH;
744 #else
745 # ifdef	ECHOCTL
746 	if (on)
747 		termbuf.c_lflag &= ~ECHOCTL;
748 	else
749 		termbuf.c_lflag |= ECHOCTL;
750 # endif
751 # ifdef	TCTLECH
752 	if (on)
753 		termbuf.c_lflag &= ~TCTLECH;
754 	else
755 		termbuf.c_lflag |= TCTLECH;
756 # endif
757 #endif
758 }
759 
760 int
761 tty_iscrnl(void)
762 {
763 #ifndef	USE_TERMIO
764 	return (termbuf.sg.sg_flags & CRMOD);
765 #else
766 	return (termbuf.c_iflag & ICRNL);
767 #endif
768 }
769 
770 /*
771  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
772  */
773 #if B4800 != 4800
774 #define	DECODE_BAUD
775 #endif
776 
777 #ifdef	DECODE_BAUD
778 
779 /*
780  * A table of available terminal speeds
781  */
782 struct termspeeds {
783 	int	speed;
784 	int	value;
785 } termspeeds[] = {
786 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
787 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
788 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
789 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
790 	{ 4800,   B4800 },
791 #ifdef	B7200
792 	{ 7200,  B7200 },
793 #endif
794 	{ 9600,   B9600 },
795 #ifdef	B14400
796 	{ 14400,  B14400 },
797 #endif
798 #ifdef	B19200
799 	{ 19200,  B19200 },
800 #endif
801 #ifdef	B28800
802 	{ 28800,  B28800 },
803 #endif
804 #ifdef	B38400
805 	{ 38400,  B38400 },
806 #endif
807 #ifdef	B57600
808 	{ 57600,  B57600 },
809 #endif
810 #ifdef	B115200
811 	{ 115200, B115200 },
812 #endif
813 #ifdef	B230400
814 	{ 230400, B230400 },
815 #endif
816 	{ -1,     0 }
817 };
818 #endif	/* DECODE_BAUD */
819 
820 void
821 tty_tspeed(int val)
822 {
823 #ifdef	DECODE_BAUD
824 	struct termspeeds *tp;
825 
826 	for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
827 		;
828 	if (tp->speed == -1)	/* back up to last valid value */
829 		--tp;
830 	cfsetospeed(&termbuf, tp->value);
831 #else	/* DECODE_BAUD */
832 	cfsetospeed(&termbuf, val);
833 #endif	/* DECODE_BAUD */
834 }
835 
836 void
837 tty_rspeed(int val)
838 {
839 #ifdef	DECODE_BAUD
840 	struct termspeeds *tp;
841 
842 	for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
843 		;
844 	if (tp->speed == -1)	/* back up to last valid value */
845 		--tp;
846 	cfsetispeed(&termbuf, tp->value);
847 #else	/* DECODE_BAUD */
848 	cfsetispeed(&termbuf, val);
849 #endif	/* DECODE_BAUD */
850 }
851 
852 /*
853  * getptyslave()
854  *
855  * Open the slave side of the pty, and do any initialization
856  * that is necessary.
857  */
858 static void
859 getptyslave(void)
860 {
861 	int t = -1;
862 	char erase;
863 
864 # ifdef	LINEMODE
865 	int waslm;
866 # endif
867 # ifdef	TIOCGWINSZ
868 	struct winsize ws;
869 	extern int def_row, def_col;
870 # endif
871 	extern int def_tspeed, def_rspeed;
872 	/*
873 	 * Opening the slave side may cause initilization of the
874 	 * kernel tty structure.  We need remember the state of
875 	 * 	if linemode was turned on
876 	 *	terminal window size
877 	 *	terminal speed
878 	 *	erase character
879 	 * so that we can re-set them if we need to.
880 	 */
881 # ifdef	LINEMODE
882 	waslm = tty_linemode();
883 # endif
884 	erase = termbuf.c_cc[VERASE];
885 
886 	/*
887 	 * Make sure that we don't have a controlling tty, and
888 	 * that we are the session (process group) leader.
889 	 */
890 # ifdef	TIOCNOTTY
891 	t = open(_PATH_TTY, O_RDWR);
892 	if (t >= 0) {
893 		(void) ioctl(t, TIOCNOTTY, NULL);
894 		(void) close(t);
895 	}
896 # endif
897 
898 	t = cleanopen(line);
899 	if (t < 0)
900 		fatalperror(net, line);
901 
902 
903 	/*
904 	 * set up the tty modes as we like them to be.
905 	 */
906 	init_termbuf();
907 # ifdef	TIOCGWINSZ
908 	if (def_row || def_col) {
909 		memset((char *)&ws, 0, sizeof(ws));
910 		ws.ws_col = def_col;
911 		ws.ws_row = def_row;
912 		(void)ioctl(t, TIOCSWINSZ, (char *)&ws);
913 	}
914 # endif
915 
916 	/*
917 	 * Settings for sgtty based systems
918 	 */
919 # ifndef	USE_TERMIO
920 	termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
921 # endif	/* USE_TERMIO */
922 
923 	/*
924 	 * Settings for all other termios/termio based
925 	 * systems, other than 4.4BSD.  In 4.4BSD the
926 	 * kernel does the initial terminal setup.
927 	 */
928 	tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
929 	tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
930 	if (erase)
931 		termbuf.c_cc[VERASE] = erase;
932 # ifdef	LINEMODE
933 	if (waslm)
934 		tty_setlinemode(1);
935 # endif	/* LINEMODE */
936 
937 	/*
938 	 * Set the tty modes, and make this our controlling tty.
939 	 */
940 	set_termbuf();
941 	if (login_tty(t) == -1)
942 		fatalperror(net, "login_tty");
943 	if (net > 2)
944 		(void) close(net);
945 #ifdef	AUTHENTICATION
946 #if	defined(NO_LOGIN_F) && defined(LOGIN_R)
947 	/*
948 	 * Leave the pty open so that we can write out the rlogin
949 	 * protocol for /bin/login, if the authentication works.
950 	 */
951 #else
952 	if (pty > 2) {
953 		(void) close(pty);
954 		pty = -1;
955 	}
956 #endif
957 #endif /* AUTHENTICATION */
958 }
959 
960 #ifndef	O_NOCTTY
961 #define	O_NOCTTY	0
962 #endif
963 /*
964  * Open the specified slave side of the pty,
965  * making sure that we have a clean tty.
966  */
967 int
968 cleanopen(char *li)
969 {
970 	int t;
971 
972 	/*
973 	 * Make sure that other people can't open the
974 	 * slave side of the connection.
975 	 */
976 	(void) chown(li, 0, 0);
977 	(void) chmod(li, 0600);
978 
979 	(void) revoke(li);
980 
981 	t = open(line, O_RDWR|O_NOCTTY);
982 
983 	if (t < 0)
984 		return(-1);
985 
986 	return(t);
987 }
988 
989 /*
990  * startslave(host)
991  *
992  * Given a hostname, do whatever
993  * is necessary to startup the login process on the slave side of the pty.
994  */
995 
996 /* ARGSUSED */
997 void
998 startslave(char *host, int autologin, char *autoname)
999 {
1000 	int i;
1001 
1002 #ifdef	AUTHENTICATION
1003 	if (!autoname || !autoname[0])
1004 		autologin = 0;
1005 
1006 	if (autologin < auth_level) {
1007 		fatal(net, "Authorization failed");
1008 		exit(1);
1009 	}
1010 #endif
1011 
1012 
1013 	if ((i = fork()) < 0)
1014 		fatalperror(net, "fork");
1015 	if (i) {
1016 	} else {
1017 		getptyslave();
1018 		start_login(host, autologin, autoname);
1019 		/*NOTREACHED*/
1020 	}
1021 }
1022 
1023 void
1024 init_env(void)
1025 {
1026 	char **envp;
1027 
1028 	envp = envinit;
1029 	if ((*envp = getenv("TZ")))
1030 		*envp++ -= 3;
1031 	*envp = 0;
1032 	environ = envinit;
1033 }
1034 
1035 
1036 /*
1037  * start_login(host)
1038  *
1039  * Assuming that we are now running as a child processes, this
1040  * function will turn us into the login process.
1041  */
1042 
1043 #ifndef AUTHENTICATION
1044 #define undef1 __unused
1045 #else
1046 #define undef1
1047 #endif
1048 
1049 void
1050 start_login(char *host undef1, int autologin undef1, char *name undef1)
1051 {
1052 	char **argv;
1053 
1054 	scrub_env();
1055 
1056 	/*
1057 	 * -h : pass on name of host.
1058 	 *		WARNING:  -h is accepted by login if and only if
1059 	 *			getuid() == 0.
1060 	 * -p : don't clobber the environment (so terminal type stays set).
1061 	 *
1062 	 * -f : force this login, he has already been authenticated
1063 	 */
1064 	argv = addarg(0, "login");
1065 
1066 #if	!defined(NO_LOGIN_H)
1067 #ifdef	AUTHENTICATION
1068 # if	defined(NO_LOGIN_F) && defined(LOGIN_R)
1069 	/*
1070 	 * Don't add the "-h host" option if we are going
1071 	 * to be adding the "-r host" option down below...
1072 	 */
1073 	if ((auth_level < 0) || (autologin != AUTH_VALID))
1074 # endif
1075 	{
1076 		argv = addarg(argv, "-h");
1077 		argv = addarg(argv, host);
1078 	}
1079 #endif /* AUTHENTICATION */
1080 #endif
1081 #if	!defined(NO_LOGIN_P)
1082 	argv = addarg(argv, "-p");
1083 #endif
1084 #ifdef	LINEMODE
1085 	/*
1086 	 * Set the environment variable "LINEMODE" to either
1087 	 * "real" or "kludge" if we are operating in either
1088 	 * real or kludge linemode.
1089 	 */
1090 	if (lmodetype == REAL_LINEMODE) {
1091 		if (setenv("LINEMODE", "real", 1) == -1)
1092 			syslog(LOG_ERR, "setenv: cannot set LINEMODE=real: %m");
1093 	}
1094 # ifdef KLUDGELINEMODE
1095 	else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) {
1096 		if (setenv("LINEMODE", "kludge", 1) == -1)
1097 			syslog(LOG_ERR, "setenv: cannot set LINEMODE=kludge: %m");
1098 	}
1099 # endif
1100 #endif
1101 #ifdef	BFTPDAEMON
1102 	/*
1103 	 * Are we working as the bftp daemon?  If so, then ask login
1104 	 * to start bftp instead of shell.
1105 	 */
1106 	if (bftpd) {
1107 		argv = addarg(argv, "-e");
1108 		argv = addarg(argv, BFTPPATH);
1109 	} else
1110 #endif
1111 #ifdef	AUTHENTICATION
1112 	if (auth_level >= 0 && autologin == AUTH_VALID) {
1113 # if	!defined(NO_LOGIN_F)
1114 		argv = addarg(argv, "-f");
1115 		argv = addarg(argv, "--");
1116 		argv = addarg(argv, name);
1117 # else
1118 #  if defined(LOGIN_R)
1119 		/*
1120 		 * We don't have support for "login -f", but we
1121 		 * can fool /bin/login into thinking that we are
1122 		 * rlogind, and allow us to log in without a
1123 		 * password.  The rlogin protocol expects
1124 		 *	local-user\0remote-user\0term/speed\0
1125 		 */
1126 
1127 		if (pty > 2) {
1128 			char *cp;
1129 			char speed[128];
1130 			int isecho, israw, xpty, len;
1131 			extern int def_rspeed;
1132 #  ifndef LOGIN_HOST
1133 			/*
1134 			 * Tell login that we are coming from "localhost".
1135 			 * If we passed in the real host name, then the
1136 			 * user would have to allow .rhost access from
1137 			 * every machine that they want authenticated
1138 			 * access to work from, which sort of defeats
1139 			 * the purpose of an authenticated login...
1140 			 * So, we tell login that the session is coming
1141 			 * from "localhost", and the user will only have
1142 			 * to have "localhost" in their .rhost file.
1143 			 */
1144 #			define LOGIN_HOST "localhost"
1145 #  endif
1146 			argv = addarg(argv, "-r");
1147 			argv = addarg(argv, LOGIN_HOST);
1148 
1149 			xpty = pty;
1150 			pty = 0;
1151 			init_termbuf();
1152 			isecho = tty_isecho();
1153 			israw = tty_israw();
1154 			if (isecho || !israw) {
1155 				tty_setecho(0);		/* Turn off echo */
1156 				tty_setraw(1);		/* Turn on raw */
1157 				set_termbuf();
1158 			}
1159 			len = strlen(name)+1;
1160 			write(xpty, name, len);
1161 			write(xpty, name, len);
1162 			snprintf(speed, sizeof(speed),
1163 				"%s/%d", (cp = getenv("TERM")) ? cp : "",
1164 				(def_rspeed > 0) ? def_rspeed : 9600);
1165 			len = strlen(speed)+1;
1166 			write(xpty, speed, len);
1167 
1168 			if (isecho || !israw) {
1169 				init_termbuf();
1170 				tty_setecho(isecho);
1171 				tty_setraw(israw);
1172 				set_termbuf();
1173 				if (!israw) {
1174 					/*
1175 					 * Write a newline to ensure
1176 					 * that login will be able to
1177 					 * read the line...
1178 					 */
1179 					write(xpty, "\n", 1);
1180 				}
1181 			}
1182 			pty = xpty;
1183 		}
1184 #  else
1185 		argv = addarg(argv, "--");
1186 		argv = addarg(argv, name);
1187 #  endif
1188 # endif
1189 	} else
1190 #endif
1191 	if (getenv("USER")) {
1192  		argv = addarg(argv, "--");
1193 		argv = addarg(argv, getenv("USER"));
1194 #if	defined(LOGIN_ARGS) && defined(NO_LOGIN_P)
1195 		{
1196 			char **cpp;
1197 			for (cpp = environ; *cpp; cpp++)
1198 				argv = addarg(argv, *cpp);
1199 		}
1200 #endif
1201 		/*
1202 		 * Assume that login will set the USER variable
1203 		 * correctly.  For SysV systems, this means that
1204 		 * USER will no longer be set, just LOGNAME by
1205 		 * login.  (The problem is that if the auto-login
1206 		 * fails, and the user then specifies a different
1207 		 * account name, he can get logged in with both
1208 		 * LOGNAME and USER in his environment, but the
1209 		 * USER value will be wrong.
1210 		 */
1211 		unsetenv("USER");
1212 	}
1213 #ifdef	AUTHENTICATION
1214 #if	defined(NO_LOGIN_F) && defined(LOGIN_R)
1215 	if (pty > 2)
1216 		close(pty);
1217 #endif
1218 #endif /* AUTHENTICATION */
1219 	closelog();
1220 
1221 	if (altlogin == NULL) {
1222 		altlogin = _PATH_LOGIN;
1223 	}
1224 	execv(altlogin, argv);
1225 
1226 	syslog(LOG_ERR, "%s: %m", altlogin);
1227 	fatalperror(net, altlogin);
1228 	/*NOTREACHED*/
1229 }
1230 
1231 static char **
1232 addarg(char **argv, const char *val)
1233 {
1234 	char **cpp;
1235 
1236 	if (argv == NULL) {
1237 		/*
1238 		 * 10 entries, a leading length, and a null
1239 		 */
1240 		argv = (char **)malloc(sizeof(*argv) * 12);
1241 		if (argv == NULL)
1242 			return(NULL);
1243 		*argv++ = (char *)10;
1244 		*argv = NULL;
1245 	}
1246 	for (cpp = argv; *cpp; cpp++)
1247 		;
1248 	if (cpp == &argv[(long)argv[-1]]) {
1249 		--argv;
1250 		*argv = (char *)((long)(*argv) + 10);
1251 		argv = (char **)realloc(argv, sizeof(*argv)*((long)(*argv) + 2));
1252 		if (argv == NULL)
1253 			return(NULL);
1254 		argv++;
1255 		cpp = &argv[(long)argv[-1] - 10];
1256 	}
1257 	*cpp++ = strdup(val);
1258 	*cpp = 0;
1259 	return(argv);
1260 }
1261 
1262 /*
1263  * scrub_env()
1264  *
1265  * We only accept the environment variables listed below.
1266  */
1267 void
1268 scrub_env(void)
1269 {
1270 	static const char *rej[] = {
1271 		"TERMCAP=/",
1272 		NULL
1273 	};
1274 
1275 	static const char *acc[] = {
1276 		"XAUTH=", "XAUTHORITY=", "DISPLAY=",
1277 		"TERM=",
1278 		"EDITOR=",
1279 		"PAGER=",
1280 		"LOGNAME=",
1281 		"POSIXLY_CORRECT=",
1282 		"PRINTER=",
1283 		NULL
1284 	};
1285 
1286 	char **cpp, **cpp2;
1287 	const char **p;
1288 	char ** new_environ;
1289 	size_t count;
1290 
1291 	/* Allocate space for scrubbed environment. */
1292 	for (count = 1, cpp = environ; *cpp; count++, cpp++)
1293 		continue;
1294 	if ((new_environ = malloc(count * sizeof(char *))) == NULL) {
1295 		environ = NULL;
1296 		return;
1297 	}
1298 
1299 	for (cpp2 = new_environ, cpp = environ; *cpp; cpp++) {
1300 		int reject_it = 0;
1301 
1302 		for(p = rej; *p; p++)
1303 			if(strncmp(*cpp, *p, strlen(*p)) == 0) {
1304 				reject_it = 1;
1305 				break;
1306 			}
1307 		if (reject_it)
1308 			continue;
1309 
1310 		for(p = acc; *p; p++)
1311 			if(strncmp(*cpp, *p, strlen(*p)) == 0)
1312 				break;
1313 		if(*p != NULL) {
1314 			if ((*cpp2++ = strdup(*cpp)) == NULL) {
1315 				environ = new_environ;
1316 				return;
1317 			}
1318 		}
1319  	}
1320 	*cpp2 = NULL;
1321 	environ = new_environ;
1322 }
1323 
1324 /*
1325  * cleanup()
1326  *
1327  * This is the routine to call when we are all through, to
1328  * clean up anything that needs to be cleaned up.
1329  */
1330 /* ARGSUSED */
1331 void
1332 cleanup(int sig __unused)
1333 {
1334 	char *p;
1335 	sigset_t mask;
1336 
1337 	p = line + sizeof(_PATH_DEV) - 1;
1338 	/*
1339 	 * Block all signals before clearing the utmp entry.  We don't want to
1340 	 * be called again after calling logout() and then not add the wtmp
1341 	 * entry because of not finding the corresponding entry in utmp.
1342 	 */
1343 	sigfillset(&mask);
1344 	sigprocmask(SIG_SETMASK, &mask, NULL);
1345 	if (logout(p))
1346 		logwtmp(p, "", "");
1347 	(void)chmod(line, 0666);
1348 	(void)chown(line, 0, 0);
1349 	*p = 'p';
1350 	(void)chmod(line, 0666);
1351 	(void)chown(line, 0, 0);
1352 	(void) shutdown(net, SHUT_RDWR);
1353 	_exit(1);
1354 }
1355