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