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