FreeBSD-SA-09:05.telnet - fix environment based code execution vulnerability
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / telnet / telnetd / sys_term.c
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
34 #include "telnetd.h"
35
36 RCSID("$Id: sys_term.c,v 1.104 2001/09/17 02:09:04 assar Exp $");
37
38 #if defined(_CRAY) || (defined(__hpux) && !defined(HAVE_UTMPX_H))
39 # define PARENT_DOES_UTMP
40 #endif
41
42 #ifdef HAVE_UTMP_H
43 #include <utmp.h>
44 #endif
45
46 #ifdef HAVE_UTMPX_H
47 #include <utmpx.h>
48 #endif
49
50 #ifdef HAVE_UTMPX_H
51 struct  utmpx wtmp;
52 #elif defined(HAVE_UTMP_H)
53 struct  utmp wtmp;
54 #endif /* HAVE_UTMPX_H */
55
56 #ifdef HAVE_STRUCT_UTMP_UT_HOST
57 int     utmp_len = sizeof(wtmp.ut_host);
58 #else
59 int     utmp_len = MaxHostNameLen;
60 #endif
61
62 #ifndef UTMP_FILE
63 #ifdef _PATH_UTMP
64 #define UTMP_FILE _PATH_UTMP
65 #else
66 #define UTMP_FILE "/etc/utmp"
67 #endif
68 #endif
69
70 #if !defined(WTMP_FILE) && defined(_PATH_WTMP)
71 #define WTMP_FILE _PATH_WTMP
72 #endif
73
74 #ifndef PARENT_DOES_UTMP
75 #ifdef WTMP_FILE
76 char    wtmpf[] = WTMP_FILE;
77 #else
78 char    wtmpf[] = "/usr/adm/wtmp";
79 #endif
80 char    utmpf[] = UTMP_FILE;
81 #else /* PARENT_DOES_UTMP */
82 #ifdef WTMP_FILE
83 char    wtmpf[] = WTMP_FILE;
84 #else
85 char    wtmpf[] = "/etc/wtmp";
86 #endif
87 #endif /* PARENT_DOES_UTMP */
88
89 #ifdef HAVE_TMPDIR_H
90 #include <tmpdir.h>
91 #endif  /* CRAY */
92
93 #ifdef  STREAMSPTY
94
95 #ifdef HAVE_SAC_H
96 #include <sac.h>
97 #endif
98
99 #ifdef HAVE_SYS_STROPTS_H
100 #include <sys/stropts.h>
101 #endif
102
103 #endif /* STREAMSPTY */
104
105 #undef NOERROR
106
107 #ifdef  HAVE_SYS_STREAM_H
108 #ifdef  HAVE_SYS_UIO_H
109 #include <sys/uio.h>
110 #endif
111 #ifdef __hpux
112 #undef SE
113 #endif
114 #include <sys/stream.h>
115 #endif
116 #if !(defined(__sgi) || defined(__linux) || defined(_AIX)) && defined(HAVE_SYS_TTY)
117 #include <sys/tty.h>
118 #endif
119 #ifdef  t_erase
120 #undef  t_erase
121 #undef  t_kill
122 #undef  t_intrc
123 #undef  t_quitc
124 #undef  t_startc
125 #undef  t_stopc
126 #undef  t_eofc
127 #undef  t_brkc
128 #undef  t_suspc
129 #undef  t_dsuspc
130 #undef  t_rprntc
131 #undef  t_flushc
132 #undef  t_werasc
133 #undef  t_lnextc
134 #endif
135
136 #ifdef HAVE_TERMIOS_H
137 #include <termios.h>
138 #else
139 #ifdef HAVE_TERMIO_H
140 #include <termio.h>
141 #endif
142 #endif
143
144 #ifdef HAVE_UTIL_H
145 #include <util.h>
146 #endif
147 #ifdef HAVE_LIBUTIL_H
148 #include <libutil.h>
149 #endif
150
151 # ifndef        TCSANOW
152 #  ifdef TCSETS
153 #   define      TCSANOW         TCSETS
154 #   define      TCSADRAIN       TCSETSW
155 #   define      tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
156 #  else
157 #   ifdef TCSETA
158 #    define     TCSANOW         TCSETA
159 #    define     TCSADRAIN       TCSETAW
160 #    define     tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
161 #   else
162 #    define     TCSANOW         TIOCSETA
163 #    define     TCSADRAIN       TIOCSETAW
164 #    define     tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
165 #   endif
166 #  endif
167 #  define       tcsetattr(f, a, t)      ioctl(f, a, t)
168 #  define       cfsetospeed(tp, val)    (tp)->c_cflag &= ~CBAUD; \
169 (tp)->c_cflag |= (val)
170 #  define       cfgetospeed(tp)         ((tp)->c_cflag & CBAUD)
171 #  ifdef CIBAUD
172 #   define      cfsetispeed(tp, val)    (tp)->c_cflag &= ~CIBAUD; \
173      (tp)->c_cflag |= ((val)<<IBSHIFT)
174 #   define      cfgetispeed(tp)         (((tp)->c_cflag & CIBAUD)>>IBSHIFT)
175 #  else
176 #   define      cfsetispeed(tp, val)    (tp)->c_cflag &= ~CBAUD; \
177      (tp)->c_cflag |= (val)
178 #   define      cfgetispeed(tp)         ((tp)->c_cflag & CBAUD)
179 #  endif
180 # endif /* TCSANOW */
181      struct termios termbuf, termbuf2;  /* pty control structure */
182 # ifdef  STREAMSPTY
183      static int ttyfd = -1;
184      int really_stream = 0;
185 # endif
186
187      const char *new_login = _PATH_LOGIN;
188
189 /*
190  * init_termbuf()
191  * copy_termbuf(cp)
192  * set_termbuf()
193  *
194  * These three routines are used to get and set the "termbuf" structure
195  * to and from the kernel.  init_termbuf() gets the current settings.
196  * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
197  * set_termbuf() writes the structure into the kernel.
198  */
199
200      void
201      init_termbuf(void)
202 {
203 # ifdef  STREAMSPTY
204     if (really_stream)
205         tcgetattr(ttyfd, &termbuf);
206     else
207 # endif
208         tcgetattr(ourpty, &termbuf);
209     termbuf2 = termbuf;
210 }
211
212 void
213 set_termbuf(void)
214 {
215     /*
216      * Only make the necessary changes.
217          */
218     if (memcmp(&termbuf, &termbuf2, sizeof(termbuf)))
219 # ifdef  STREAMSPTY
220         if (really_stream)
221             tcsetattr(ttyfd, TCSANOW, &termbuf);
222         else
223 # endif
224             tcsetattr(ourpty, TCSANOW, &termbuf);
225 }
226
227
228 /*
229  * spcset(func, valp, valpp)
230  *
231  * This function takes various special characters (func), and
232  * sets *valp to the current value of that character, and
233  * *valpp to point to where in the "termbuf" structure that
234  * value is kept.
235  *
236  * It returns the SLC_ level of support for this function.
237  */
238
239
240 int
241 spcset(int func, cc_t *valp, cc_t **valpp)
242 {
243
244 #define setval(a, b)    *valp = termbuf.c_cc[a]; \
245     *valpp = &termbuf.c_cc[a]; \
246                                    return(b);
247 #define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
248
249     switch(func) {
250     case SLC_EOF:
251         setval(VEOF, SLC_VARIABLE);
252     case SLC_EC:
253         setval(VERASE, SLC_VARIABLE);
254     case SLC_EL:
255         setval(VKILL, SLC_VARIABLE);
256     case SLC_IP:
257         setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
258     case SLC_ABORT:
259         setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
260     case SLC_XON:
261 #ifdef  VSTART
262         setval(VSTART, SLC_VARIABLE);
263 #else
264         defval(0x13);
265 #endif
266     case SLC_XOFF:
267 #ifdef  VSTOP
268         setval(VSTOP, SLC_VARIABLE);
269 #else
270         defval(0x11);
271 #endif
272     case SLC_EW:
273 #ifdef  VWERASE
274         setval(VWERASE, SLC_VARIABLE);
275 #else
276         defval(0);
277 #endif
278     case SLC_RP:
279 #ifdef  VREPRINT
280         setval(VREPRINT, SLC_VARIABLE);
281 #else
282         defval(0);
283 #endif
284     case SLC_LNEXT:
285 #ifdef  VLNEXT
286         setval(VLNEXT, SLC_VARIABLE);
287 #else
288         defval(0);
289 #endif
290     case SLC_AO:
291 #if     !defined(VDISCARD) && defined(VFLUSHO)
292 # define VDISCARD VFLUSHO
293 #endif
294 #ifdef  VDISCARD
295         setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
296 #else
297         defval(0);
298 #endif
299     case SLC_SUSP:
300 #ifdef  VSUSP
301         setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
302 #else
303         defval(0);
304 #endif
305 #ifdef  VEOL
306     case SLC_FORW1:
307         setval(VEOL, SLC_VARIABLE);
308 #endif
309 #ifdef  VEOL2
310     case SLC_FORW2:
311         setval(VEOL2, SLC_VARIABLE);
312 #endif
313     case SLC_AYT:
314 #ifdef  VSTATUS
315         setval(VSTATUS, SLC_VARIABLE);
316 #else
317         defval(0);
318 #endif
319
320     case SLC_BRK:
321     case SLC_SYNCH:
322     case SLC_EOR:
323         defval(0);
324
325     default:
326         *valp = 0;
327         *valpp = 0;
328         return(SLC_NOSUPPORT);
329     }
330 }
331
332 #ifdef _CRAY
333 /*
334  * getnpty()
335  *
336  * Return the number of pty's configured into the system.
337  */
338 int
339 getnpty()
340 {
341 #ifdef _SC_CRAY_NPTY
342     int numptys;
343
344     if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1)
345         return numptys;
346     else
347 #endif /* _SC_CRAY_NPTY */
348         return 128;
349 }
350 #endif /* CRAY */
351
352 /*
353  * getpty()
354  *
355  * Allocate a pty.  As a side effect, the external character
356  * array "line" contains the name of the slave side.
357  *
358  * Returns the file descriptor of the opened pty.
359  */
360
361 static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
362 char *line = Xline;
363
364 #ifdef  _CRAY
365 char myline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
366 #endif  /* CRAY */
367
368 #if !defined(HAVE_PTSNAME) && defined(STREAMSPTY)
369 static char *ptsname(int fd)
370 {
371 #ifdef HAVE_TTYNAME
372     return ttyname(fd);
373 #else
374     return NULL;
375 #endif
376 }
377 #endif
378
379 int getpty(int *ptynum)
380 {
381 #ifdef __osf__ /* XXX */
382     int master;
383     int slave;
384     if(openpty(&master, &slave, line, 0, 0) == 0){
385         close(slave);
386         return master;
387     }
388     return -1;
389 #else
390 #ifdef HAVE__GETPTY
391     int master, slave;
392     char *p;
393     p = _getpty(&master, O_RDWR, 0600, 1);
394     if(p == NULL)
395         return -1;
396     strlcpy(line, p, sizeof(Xline));
397     return master;
398 #else
399
400     int p;
401     char *cp, *p1, *p2;
402     int i;
403 #if SunOS == 40
404     int dummy;
405 #endif
406 #if __linux
407     int master;
408     int slave;
409     if(openpty(&master, &slave, line, 0, 0) == 0){
410         close(slave);
411         return master;
412     }
413 #else
414 #ifdef  STREAMSPTY
415     char *clone[] = { "/dev/ptc", "/dev/ptmx", "/dev/ptm", 
416                       "/dev/ptym/clone", 0 };
417
418     char **q;
419     for(q=clone; *q; q++){
420         p=open(*q, O_RDWR);
421         if(p >= 0){
422 #ifdef HAVE_GRANTPT
423             grantpt(p);
424 #endif
425 #ifdef HAVE_UNLOCKPT
426             unlockpt(p);
427 #endif
428             strlcpy(line, ptsname(p), sizeof(Xline));
429             really_stream = 1;
430             return p;
431         }
432     }
433 #endif /* STREAMSPTY */
434 #ifndef _CRAY
435
436 #ifndef __hpux
437     snprintf(line, sizeof(Xline), "/dev/ptyXX");
438     p1 = &line[8];
439     p2 = &line[9];
440 #else
441     snprintf(line, sizeof(Xline), "/dev/ptym/ptyXX");
442     p1 = &line[13];
443     p2 = &line[14];
444 #endif
445
446         
447     for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) {
448         struct stat stb;
449
450         *p1 = *cp;
451         *p2 = '0';
452         /*
453          * This stat() check is just to keep us from
454          * looping through all 256 combinations if there
455          * aren't that many ptys available.
456          */
457         if (stat(line, &stb) < 0)
458             break;
459         for (i = 0; i < 16; i++) {
460             *p2 = "0123456789abcdef"[i];
461             p = open(line, O_RDWR);
462             if (p > 0) {
463 #ifndef __hpux
464                 line[5] = 't';
465 #else
466                 for (p1 = &line[8]; *p1; p1++)
467                     *p1 = *(p1+1);
468                 line[9] = 't';
469 #endif
470                 chown(line, 0, 0);
471                 chmod(line, 0600);
472 #if SunOS == 40
473                 if (ioctl(p, TIOCGPGRP, &dummy) == 0
474                     || errno != EIO) {
475                     chmod(line, 0666);
476                     close(p);
477                     line[5] = 'p';
478                 } else
479 #endif /* SunOS == 40 */
480                     return(p);
481             }
482         }
483     }
484 #else   /* CRAY */
485     extern lowpty, highpty;
486     struct stat sb;
487
488     for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) {
489         snprintf(myline, sizeof(myline), "/dev/pty/%03d", *ptynum);
490         p = open(myline, 2);
491         if (p < 0)
492             continue;
493         snprintf(line, sizeof(Xline), "/dev/ttyp%03d", *ptynum);
494         /*
495          * Here are some shenanigans to make sure that there
496          * are no listeners lurking on the line.
497          */
498         if(stat(line, &sb) < 0) {
499             close(p);
500             continue;
501         }
502         if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) {
503             chown(line, 0, 0);
504             chmod(line, 0600);
505             close(p);
506             p = open(myline, 2);
507             if (p < 0)
508                 continue;
509         }
510         /*
511          * Now it should be safe...check for accessability.
512          */
513         if (access(line, 6) == 0)
514             return(p);
515         else {
516             /* no tty side to pty so skip it */
517             close(p);
518         }
519     }
520 #endif  /* CRAY */
521 #endif  /* STREAMSPTY */
522 #endif /* OPENPTY */
523     return(-1);
524 #endif
525 }
526
527
528 int
529 tty_isecho(void)
530 {
531     return (termbuf.c_lflag & ECHO);
532 }
533
534 int
535 tty_flowmode(void)
536 {
537     return((termbuf.c_iflag & IXON) ? 1 : 0);
538 }
539
540 int
541 tty_restartany(void)
542 {
543     return((termbuf.c_iflag & IXANY) ? 1 : 0);
544 }
545
546 void
547 tty_setecho(int on)
548 {
549     if (on)
550         termbuf.c_lflag |= ECHO;
551     else
552         termbuf.c_lflag &= ~ECHO;
553 }
554
555 int
556 tty_israw(void)
557 {
558     return(!(termbuf.c_lflag & ICANON));
559 }
560
561 void
562 tty_binaryin(int on)
563 {
564     if (on) {
565         termbuf.c_iflag &= ~ISTRIP;
566     } else {
567         termbuf.c_iflag |= ISTRIP;
568     }
569 }
570
571 void
572 tty_binaryout(int on)
573 {
574     if (on) {
575         termbuf.c_cflag &= ~(CSIZE|PARENB);
576         termbuf.c_cflag |= CS8;
577         termbuf.c_oflag &= ~OPOST;
578     } else {
579         termbuf.c_cflag &= ~CSIZE;
580         termbuf.c_cflag |= CS7|PARENB;
581         termbuf.c_oflag |= OPOST;
582     }
583 }
584
585 int
586 tty_isbinaryin(void)
587 {
588     return(!(termbuf.c_iflag & ISTRIP));
589 }
590
591 int
592 tty_isbinaryout(void)
593 {
594     return(!(termbuf.c_oflag&OPOST));
595 }
596
597
598 int
599 tty_issofttab(void)
600 {
601 # ifdef OXTABS
602     return (termbuf.c_oflag & OXTABS);
603 # endif
604 # ifdef TABDLY
605     return ((termbuf.c_oflag & TABDLY) == TAB3);
606 # endif
607 }
608
609 void
610 tty_setsofttab(int on)
611 {
612     if (on) {
613 # ifdef OXTABS
614         termbuf.c_oflag |= OXTABS;
615 # endif
616 # ifdef TABDLY
617         termbuf.c_oflag &= ~TABDLY;
618         termbuf.c_oflag |= TAB3;
619 # endif
620     } else {
621 # ifdef OXTABS
622         termbuf.c_oflag &= ~OXTABS;
623 # endif
624 # ifdef TABDLY
625         termbuf.c_oflag &= ~TABDLY;
626         termbuf.c_oflag |= TAB0;
627 # endif
628     }
629 }
630
631 int
632 tty_islitecho(void)
633 {
634 # ifdef ECHOCTL
635     return (!(termbuf.c_lflag & ECHOCTL));
636 # endif
637 # ifdef TCTLECH
638     return (!(termbuf.c_lflag & TCTLECH));
639 # endif
640 # if    !defined(ECHOCTL) && !defined(TCTLECH)
641     return (0); /* assumes ctl chars are echoed '^x' */
642 # endif
643 }
644
645 void
646 tty_setlitecho(int on)
647 {
648 # ifdef ECHOCTL
649     if (on)
650         termbuf.c_lflag &= ~ECHOCTL;
651     else
652         termbuf.c_lflag |= ECHOCTL;
653 # endif
654 # ifdef TCTLECH
655     if (on)
656         termbuf.c_lflag &= ~TCTLECH;
657     else
658         termbuf.c_lflag |= TCTLECH;
659 # endif
660 }
661
662 int
663 tty_iscrnl(void)
664 {
665     return (termbuf.c_iflag & ICRNL);
666 }
667
668 /*
669  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
670  */
671 #if B4800 != 4800
672 #define DECODE_BAUD
673 #endif
674
675 #ifdef  DECODE_BAUD
676
677 /*
678  * A table of available terminal speeds
679  */
680 struct termspeeds {
681     int speed;
682     int value;
683 } termspeeds[] = {
684     { 0,      B0 },      { 50,    B50 },    { 75,     B75 },
685     { 110,    B110 },    { 134,   B134 },   { 150,    B150 },
686     { 200,    B200 },    { 300,   B300 },   { 600,    B600 },
687     { 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
688     { 4800,   B4800 },
689 #ifdef  B7200
690     { 7200,  B7200 },
691 #endif
692     { 9600,   B9600 },
693 #ifdef  B14400
694     { 14400,  B14400 },
695 #endif
696 #ifdef  B19200
697     { 19200,  B19200 },
698 #endif
699 #ifdef  B28800
700     { 28800,  B28800 },
701 #endif
702 #ifdef  B38400
703     { 38400,  B38400 },
704 #endif
705 #ifdef  B57600
706     { 57600,  B57600 },
707 #endif
708 #ifdef  B115200
709     { 115200, B115200 },
710 #endif
711 #ifdef  B230400
712     { 230400, B230400 },
713 #endif
714     { -1,     0 }
715 };
716 #endif  /* DECODE_BUAD */
717
718 void
719 tty_tspeed(int val)
720 {
721 #ifdef  DECODE_BAUD
722     struct termspeeds *tp;
723
724     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
725         ;
726     if (tp->speed == -1)        /* back up to last valid value */
727         --tp;
728     cfsetospeed(&termbuf, tp->value);
729 #else   /* DECODE_BUAD */
730     cfsetospeed(&termbuf, val);
731 #endif  /* DECODE_BUAD */
732 }
733
734 void
735 tty_rspeed(int val)
736 {
737 #ifdef  DECODE_BAUD
738     struct termspeeds *tp;
739
740     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
741         ;
742     if (tp->speed == -1)        /* back up to last valid value */
743         --tp;
744     cfsetispeed(&termbuf, tp->value);
745 #else   /* DECODE_BAUD */
746     cfsetispeed(&termbuf, val);
747 #endif  /* DECODE_BAUD */
748 }
749
750 #ifdef PARENT_DOES_UTMP
751 extern  struct utmp wtmp;
752 extern char wtmpf[];
753
754 extern void utmp_sig_init (void);
755 extern void utmp_sig_reset (void);
756 extern void utmp_sig_wait (void);
757 extern void utmp_sig_notify (int);
758 # endif /* PARENT_DOES_UTMP */
759
760 #ifdef STREAMSPTY
761
762 /* I_FIND seems to live a life of its own */
763 static int my_find(int fd, char *module)
764 {
765 #if defined(I_FIND) && defined(I_LIST)
766     static int flag;
767     static struct str_list sl;
768     int n;
769     int i;
770   
771     if(!flag){
772         n = ioctl(fd, I_LIST, 0);
773         if(n < 0){
774             perror("ioctl(fd, I_LIST, 0)");
775             return -1;
776         }
777         sl.sl_modlist=(struct str_mlist*)malloc(n * sizeof(struct str_mlist));
778         sl.sl_nmods = n;
779         n = ioctl(fd, I_LIST, &sl);
780         if(n < 0){
781             perror("ioctl(fd, I_LIST, n)");
782             return -1;
783         }
784         flag = 1;
785     }
786   
787     for(i=0; i<sl.sl_nmods; i++)
788         if(!strcmp(sl.sl_modlist[i].l_name, module))
789             return 1;
790 #endif
791     return 0;
792 }
793
794 static void maybe_push_modules(int fd, char **modules)
795 {
796     char **p;
797     int err;
798
799     for(p=modules; *p; p++){
800         err = my_find(fd, *p);
801         if(err == 1)
802             break;
803         if(err < 0 && errno != EINVAL)
804             fatalperror(net, "my_find()");
805         /* module not pushed or does not exist */
806     }
807     /* p points to null or to an already pushed module, now push all
808        modules before this one */
809   
810     for(p--; p >= modules; p--){
811         err = ioctl(fd, I_PUSH, *p);
812         if(err < 0 && errno != EINVAL)
813             fatalperror(net, "I_PUSH");
814     }
815 }
816 #endif
817
818 /*
819  * getptyslave()
820  *
821  * Open the slave side of the pty, and do any initialization
822  * that is necessary.  The return value is a file descriptor
823  * for the slave side.
824  */
825 void getptyslave(void)
826 {
827     int t = -1;
828
829     struct winsize ws;
830     /*
831      * Opening the slave side may cause initilization of the
832      * kernel tty structure.  We need remember the state of
833      *  if linemode was turned on
834      *  terminal window size
835      *  terminal speed
836      * so that we can re-set them if we need to.
837      */
838
839
840     /*
841      * Make sure that we don't have a controlling tty, and
842      * that we are the session (process group) leader.
843      */
844
845 #ifdef HAVE_SETSID
846     if(setsid()<0)
847         fatalperror(net, "setsid()");
848 #else
849 # ifdef TIOCNOTTY
850     t = open(_PATH_TTY, O_RDWR);
851     if (t >= 0) {
852         ioctl(t, TIOCNOTTY, (char *)0);
853         close(t);
854     }
855 # endif
856 #endif
857
858 # ifdef PARENT_DOES_UTMP
859     /*
860      * Wait for our parent to get the utmp stuff to get done.
861      */
862     utmp_sig_wait();
863 # endif
864
865     t = cleanopen(line);
866     if (t < 0)
867         fatalperror(net, line);
868
869 #ifdef  STREAMSPTY
870     ttyfd = t;
871           
872
873     /*
874      * Not all systems have (or need) modules ttcompat and pckt so
875      * don't flag it as a fatal error if they don't exist.
876      */
877
878     if (really_stream)
879         {
880             /* these are the streams modules that we want pushed. note
881                that they are in reverse order, ptem will be pushed
882                first. maybe_push_modules() will try to push all modules
883                before the first one that isn't already pushed. i.e if
884                ldterm is pushed, only ttcompat will be attempted.
885
886                all this is because we don't know which modules are
887                available, and we don't know which modules are already
888                pushed (via autopush, for instance).
889
890                */
891              
892             char *ttymodules[] = { "ttcompat", "ldterm", "ptem", NULL };
893             char *ptymodules[] = { "pckt", NULL };
894
895             maybe_push_modules(t, ttymodules);
896             maybe_push_modules(ourpty, ptymodules);
897         }
898 #endif
899     /*
900      * set up the tty modes as we like them to be.
901      */
902     init_termbuf();
903 # ifdef TIOCSWINSZ
904     if (def_row || def_col) {
905         memset(&ws, 0, sizeof(ws));
906         ws.ws_col = def_col;
907         ws.ws_row = def_row;
908         ioctl(t, TIOCSWINSZ, (char *)&ws);
909     }
910 # endif
911
912     /*
913      * Settings for sgtty based systems
914      */
915
916     /*
917      * Settings for UNICOS (and HPUX)
918      */
919 # if defined(_CRAY) || defined(__hpux)
920     termbuf.c_oflag = OPOST|ONLCR|TAB3;
921     termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
922     termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
923     termbuf.c_cflag = EXTB|HUPCL|CS8;
924 # endif
925
926     /*
927      * Settings for all other termios/termio based
928      * systems, other than 4.4BSD.  In 4.4BSD the
929      * kernel does the initial terminal setup.
930      */
931 # if !(defined(_CRAY) || defined(__hpux)) && (BSD <= 43)
932 #  ifndef       OXTABS
933 #   define OXTABS       0
934 #  endif
935     termbuf.c_lflag |= ECHO;
936     termbuf.c_oflag |= ONLCR|OXTABS;
937     termbuf.c_iflag |= ICRNL;
938     termbuf.c_iflag &= ~IXOFF;
939 # endif
940     tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
941     tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
942
943     /*
944      * Set the tty modes, and make this our controlling tty.
945      */
946     set_termbuf();
947     if (login_tty(t) == -1)
948         fatalperror(net, "login_tty");
949     if (net > 2)
950         close(net);
951     if (ourpty > 2) {
952         close(ourpty);
953         ourpty = -1;
954     }
955 }
956
957 #ifndef O_NOCTTY
958 #define O_NOCTTY        0
959 #endif
960 /*
961  * Open the specified slave side of the pty,
962  * making sure that we have a clean tty.
963  */
964
965 int cleanopen(char *line)
966 {
967     int t;
968
969 #ifdef STREAMSPTY
970     if (!really_stream)
971 #endif
972         {
973             /*
974              * Make sure that other people can't open the
975              * slave side of the connection.
976              */
977             chown(line, 0, 0);
978             chmod(line, 0600);
979         }
980
981 #ifdef HAVE_REVOKE
982     revoke(line);
983 #endif
984
985     t = open(line, O_RDWR|O_NOCTTY);
986
987     if (t < 0)
988         return(-1);
989
990     /*
991      * Hangup anybody else using this ttyp, then reopen it for
992      * ourselves.
993      */
994 # if !(defined(_CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY)
995     signal(SIGHUP, SIG_IGN);
996 #ifdef HAVE_VHANGUP
997     vhangup();
998 #else
999 #endif
1000     signal(SIGHUP, SIG_DFL);
1001     t = open(line, O_RDWR|O_NOCTTY);
1002     if (t < 0)
1003         return(-1);
1004 # endif
1005 # if    defined(_CRAY) && defined(TCVHUP)
1006     {
1007         int i;
1008         signal(SIGHUP, SIG_IGN);
1009         ioctl(t, TCVHUP, (char *)0);
1010         signal(SIGHUP, SIG_DFL);
1011
1012         i = open(line, O_RDWR);
1013
1014         if (i < 0)
1015             return(-1);
1016         close(t);
1017         t = i;
1018     }
1019 # endif /* defined(CRAY) && defined(TCVHUP) */
1020     return(t);
1021 }
1022
1023 #if !defined(BSD4_4)
1024
1025 int login_tty(int t)
1026 {
1027 # if defined(TIOCSCTTY) && !defined(__hpux)
1028     if (ioctl(t, TIOCSCTTY, (char *)0) < 0)
1029         fatalperror(net, "ioctl(sctty)");
1030 #  ifdef _CRAY
1031     /*
1032      * Close the hard fd to /dev/ttypXXX, and re-open through
1033      * the indirect /dev/tty interface.
1034      */
1035     close(t);
1036     if ((t = open("/dev/tty", O_RDWR)) < 0)
1037         fatalperror(net, "open(/dev/tty)");
1038 #  endif
1039 # else
1040     /*
1041      * We get our controlling tty assigned as a side-effect
1042      * of opening up a tty device.  But on BSD based systems,
1043      * this only happens if our process group is zero.  The
1044      * setsid() call above may have set our pgrp, so clear
1045      * it out before opening the tty...
1046      */
1047 #ifdef HAVE_SETPGID
1048     setpgid(0, 0);
1049 #else
1050     setpgrp(0, 0); /* if setpgid isn't available, setpgrp
1051                       probably takes arguments */
1052 #endif
1053     close(open(line, O_RDWR));
1054 # endif
1055     if (t != 0)
1056         dup2(t, 0);
1057     if (t != 1)
1058         dup2(t, 1);
1059     if (t != 2)
1060         dup2(t, 2);
1061     if (t > 2)
1062         close(t);
1063     return(0);
1064 }
1065 #endif  /* BSD <= 43 */
1066
1067 /*
1068  * This comes from ../../bsd/tty.c and should not really be here.
1069  */
1070
1071 /*
1072  * Clean the tty name.  Return a pointer to the cleaned version.
1073  */
1074
1075 static char *
1076 clean_ttyname (char *tty)
1077 {
1078   char *res = tty;
1079
1080   if (strncmp (res, _PATH_DEV, strlen(_PATH_DEV)) == 0)
1081     res += strlen(_PATH_DEV);
1082   if (strncmp (res, "pty/", 4) == 0)
1083     res += 4;
1084   if (strncmp (res, "ptym/", 5) == 0)
1085     res += 5;
1086   return res;
1087 }
1088
1089 /*
1090  * Generate a name usable as an `ut_id', typically without `tty'.
1091  */
1092
1093 #ifdef HAVE_STRUCT_UTMP_UT_ID
1094 static char *
1095 make_id (char *tty)
1096 {
1097   char *res = tty;
1098   
1099   if (strncmp (res, "pts/", 4) == 0)
1100     res += 4;
1101   if (strncmp (res, "tty", 3) == 0)
1102     res += 3;
1103   return res;
1104 }
1105 #endif
1106
1107 /*
1108  * startslave(host)
1109  *
1110  * Given a hostname, do whatever
1111  * is necessary to startup the login process on the slave side of the pty.
1112  */
1113
1114 /* ARGSUSED */
1115 void
1116 startslave(const char *host, const char *utmp_host,
1117            int autologin, char *autoname)
1118 {
1119     int i;
1120
1121 #ifdef AUTHENTICATION
1122     if (!autoname || !autoname[0])
1123         autologin = 0;
1124
1125     if (autologin < auth_level) {
1126         fatal(net, "Authorization failed");
1127         exit(1);
1128     }
1129 #endif
1130
1131     {
1132         char *tbuf =
1133             "\r\n*** Connection not encrypted! "
1134             "Communication may be eavesdropped. ***\r\n";
1135 #ifdef ENCRYPTION
1136         if (!no_warn && (encrypt_output == 0 || decrypt_input == 0))
1137 #endif
1138             writenet((unsigned char*)tbuf, strlen(tbuf));
1139     }
1140 # ifdef PARENT_DOES_UTMP
1141     utmp_sig_init();
1142 # endif /* PARENT_DOES_UTMP */
1143
1144     if ((i = fork()) < 0)
1145         fatalperror(net, "fork");
1146     if (i) {
1147 # ifdef PARENT_DOES_UTMP
1148         /*
1149          * Cray parent will create utmp entry for child and send
1150          * signal to child to tell when done.  Child waits for signal
1151          * before doing anything important.
1152          */
1153         int pid = i;
1154         void sigjob (int);
1155
1156         setpgrp();
1157         utmp_sig_reset();               /* reset handler to default */
1158         /*
1159          * Create utmp entry for child
1160          */
1161         wtmp.ut_time = time(NULL);
1162         wtmp.ut_type = LOGIN_PROCESS;
1163         wtmp.ut_pid = pid;
1164         strncpy(wtmp.ut_user,  "LOGIN", sizeof(wtmp.ut_user));
1165         strncpy(wtmp.ut_host,  utmp_host, sizeof(wtmp.ut_host));
1166         strncpy(wtmp.ut_line,  clean_ttyname(line), sizeof(wtmp.ut_line));
1167 #ifdef HAVE_STRUCT_UTMP_UT_ID
1168         strncpy(wtmp.ut_id, wtmp.ut_line + 3, sizeof(wtmp.ut_id));
1169 #endif
1170
1171         pututline(&wtmp);
1172         endutent();
1173         if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) {
1174             write(i, &wtmp, sizeof(struct utmp));
1175             close(i);
1176         }
1177 #ifdef  _CRAY
1178         signal(WJSIGNAL, sigjob);
1179 #endif
1180         utmp_sig_notify(pid);
1181 # endif /* PARENT_DOES_UTMP */
1182     } else {
1183         getptyslave();
1184 #if defined(DCE)
1185         /* if we authenticated via K5, try and join the PAG */
1186         kerberos5_dfspag();
1187 #endif
1188         start_login(host, autologin, autoname);
1189         /*NOTREACHED*/
1190     }
1191 }
1192
1193 char    *envinit[3];
1194 extern char **environ;
1195
1196 void
1197 init_env(void)
1198 {
1199     char **envp;
1200
1201     envp = envinit;
1202     if ((*envp = getenv("TZ")))
1203         *envp++ -= 3;
1204 #if defined(_CRAY) || defined(__hpux)
1205     else
1206         *envp++ = "TZ=GMT0";
1207 #endif
1208     *envp = 0;
1209     environ = envinit;
1210 }
1211
1212 /*
1213  * scrub_env()
1214  *
1215  * We only accept the environment variables listed below.
1216  */
1217
1218 static void
1219 scrub_env(void)
1220 {
1221     static const char *reject[] = {
1222         "TERMCAP=/",
1223         NULL
1224     };
1225
1226     static const char *accept[] = {
1227         "XAUTH=", "XAUTHORITY=", "DISPLAY=",
1228         "TERM=",
1229         "EDITOR=",
1230         "PAGER=",
1231         "PRINTER=",
1232         "LOGNAME=",
1233         "POSIXLY_CORRECT=",
1234         "TERMCAP=",
1235         NULL
1236     };
1237
1238     char **cpp, **cpp2;
1239     const char **p;
1240     char ** new_environ;
1241     size_t count;
1242
1243     /* Allocate space for scrubbed environment. */
1244     for (count = 1, cpp = environ; *cpp; count++, cpp++)
1245         ;
1246     if ((new_environ = malloc(count * sizeof(char *))) == NULL) {
1247         environ = NULL;
1248         return;
1249     }
1250   
1251     for (cpp2 = new_environ, cpp = environ; *cpp; cpp++) {
1252         int reject_it = 0;
1253
1254         for(p = reject; *p; p++)
1255             if(strncmp(*cpp, *p, strlen(*p)) == 0) {
1256                 reject_it = 1;
1257                 break;
1258             }
1259         if (reject_it)
1260             continue;
1261
1262         for(p = accept; *p; p++)
1263             if(strncmp(*cpp, *p, strlen(*p)) == 0)
1264                 break;
1265         if(*p != NULL) {
1266                 if ((*cpp2++ = strdup(*cpp)) == NULL) {
1267                         environ = new_environ;
1268                         return;
1269                 }
1270         }
1271     }
1272     *cpp2 = NULL;
1273     environ = new_environ;
1274 }
1275
1276
1277 struct arg_val {
1278     int size;
1279     int argc;
1280     const char **argv;
1281 };
1282
1283 static void addarg(struct arg_val*, const char*);
1284
1285 /*
1286  * start_login(host)
1287  *
1288  * Assuming that we are now running as a child processes, this
1289  * function will turn us into the login process.
1290  */
1291
1292 void
1293 start_login(const char *host, int autologin, char *name)
1294 {
1295     struct arg_val argv;
1296     char *user;
1297     int save_errno;
1298
1299 #ifdef HAVE_UTMPX_H
1300     int pid = getpid();
1301     struct utmpx utmpx;
1302     char *clean_tty;
1303
1304     /*
1305      * Create utmp entry for child
1306      */
1307
1308     clean_tty = clean_ttyname(line);
1309     memset(&utmpx, 0, sizeof(utmpx));
1310     strncpy(utmpx.ut_user,  ".telnet", sizeof(utmpx.ut_user));
1311     strncpy(utmpx.ut_line,  clean_tty, sizeof(utmpx.ut_line));
1312 #ifdef HAVE_STRUCT_UTMP_UT_ID
1313     strncpy(utmpx.ut_id, make_id(clean_tty), sizeof(utmpx.ut_id));
1314 #endif
1315     utmpx.ut_pid = pid;
1316         
1317     utmpx.ut_type = LOGIN_PROCESS;
1318
1319     gettimeofday (&utmpx.ut_tv, NULL);
1320     if (pututxline(&utmpx) == NULL)
1321         fatal(net, "pututxline failed");
1322 #endif
1323
1324     scrub_env();
1325         
1326     /*
1327      * -h : pass on name of host.
1328      *          WARNING:  -h is accepted by login if and only if
1329      *                  getuid() == 0.
1330      * -p : don't clobber the environment (so terminal type stays set).
1331      *
1332      * -f : force this login, he has already been authenticated
1333      */
1334
1335     /* init argv structure */ 
1336     argv.size=0;
1337     argv.argc=0;
1338     argv.argv=malloc(0); /*so we can call realloc later */
1339     addarg(&argv, "login");
1340     addarg(&argv, "-h");
1341     addarg(&argv, host);
1342     addarg(&argv, "-p");
1343     if(name[0])
1344         user = name;
1345     else
1346         user = getenv("USER");
1347 #ifdef AUTHENTICATION
1348     if (auth_level < 0 || autologin != AUTH_VALID) {
1349         if(!no_warn) {
1350             printf("User not authenticated. ");
1351             if (require_otp)
1352                 printf("Using one-time password\r\n");
1353             else
1354                 printf("Using plaintext username and password\r\n");
1355         }
1356         if (require_otp) {
1357             addarg(&argv, "-a");
1358             addarg(&argv, "otp");
1359         }
1360         if(log_unauth) 
1361             syslog(LOG_INFO, "unauthenticated access from %s (%s)", 
1362                    host, user ? user : "unknown user");
1363     }
1364     if (auth_level >= 0 && autologin == AUTH_VALID)
1365         addarg(&argv, "-f");
1366 #endif
1367     if(user){
1368         addarg(&argv, "--");
1369         addarg(&argv, strdup(user));
1370     }
1371     if (getenv("USER")) {
1372         /*
1373          * Assume that login will set the USER variable
1374          * correctly.  For SysV systems, this means that
1375          * USER will no longer be set, just LOGNAME by
1376          * login.  (The problem is that if the auto-login
1377          * fails, and the user then specifies a different
1378          * account name, he can get logged in with both
1379          * LOGNAME and USER in his environment, but the
1380          * USER value will be wrong.
1381          */
1382         unsetenv("USER");
1383     }
1384     closelog();
1385     /*
1386      * This sleep(1) is in here so that telnetd can
1387      * finish up with the tty.  There's a race condition
1388      * the login banner message gets lost...
1389      */
1390     sleep(1);
1391
1392     execv(new_login, argv.argv);
1393     save_errno = errno;
1394     syslog(LOG_ERR, "%s: %m\n", new_login);
1395     fatalperror_errno(net, new_login, save_errno);
1396     /*NOTREACHED*/
1397 }
1398
1399 static void
1400 addarg(struct arg_val *argv, const char *val)
1401 {
1402     if(argv->size <= argv->argc+1) {
1403         argv->argv = realloc(argv->argv, sizeof(char*) * (argv->size + 10));
1404         if (argv->argv == NULL)
1405             fatal (net, "realloc: out of memory");
1406         argv->size+=10;
1407     }
1408     argv->argv[argv->argc++] = val;
1409     argv->argv[argv->argc]   = NULL;
1410 }
1411
1412
1413 /*
1414  * rmut()
1415  *
1416  * This is the function called by cleanup() to
1417  * remove the utmp entry for this person.
1418  */
1419
1420 #ifdef HAVE_UTMPX_H
1421 static void
1422 rmut(void)
1423 {
1424     struct utmpx utmpx, *non_save_utxp;
1425     char *clean_tty = clean_ttyname(line);
1426
1427     /*
1428      * This updates the utmpx and utmp entries and make a wtmp/x entry
1429      */
1430
1431     setutxent();
1432     memset(&utmpx, 0, sizeof(utmpx));
1433     strncpy(utmpx.ut_line, clean_tty, sizeof(utmpx.ut_line));
1434     utmpx.ut_type = LOGIN_PROCESS;
1435     non_save_utxp = getutxline(&utmpx);
1436     if (non_save_utxp) {
1437         struct utmpx *utxp;
1438         char user0;
1439
1440         utxp = malloc(sizeof(struct utmpx));
1441         *utxp = *non_save_utxp;
1442         user0 = utxp->ut_user[0];
1443         utxp->ut_user[0] = '\0';
1444         utxp->ut_type = DEAD_PROCESS;
1445 #ifdef HAVE_STRUCT_UTMPX_UT_EXIT
1446 #ifdef _STRUCT___EXIT_STATUS
1447         utxp->ut_exit.__e_termination = 0;
1448         utxp->ut_exit.__e_exit = 0;
1449 #elif defined(__osf__) /* XXX */
1450         utxp->ut_exit.ut_termination = 0;
1451         utxp->ut_exit.ut_exit = 0;
1452 #else   
1453         utxp->ut_exit.e_termination = 0;
1454         utxp->ut_exit.e_exit = 0;
1455 #endif
1456 #endif
1457         gettimeofday(&utxp->ut_tv, NULL);
1458         pututxline(utxp);
1459 #ifdef WTMPX_FILE
1460         utxp->ut_user[0] = user0;
1461         updwtmpx(WTMPX_FILE, utxp);
1462 #elif defined(WTMP_FILE)
1463         /* This is a strange system with a utmpx and a wtmp! */
1464         {
1465           int f = open(wtmpf, O_WRONLY|O_APPEND);
1466           struct utmp wtmp;
1467           if (f >= 0) {
1468             strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1469             strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1470 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1471             strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1472 #endif
1473             wtmp.ut_time = time(NULL);
1474             write(f, &wtmp, sizeof(wtmp));
1475             close(f);
1476           }
1477         }
1478 #endif
1479         free (utxp);
1480     }
1481     endutxent();
1482 }  /* end of rmut */
1483 #endif
1484
1485 #if !defined(HAVE_UTMPX_H) && !(defined(_CRAY) || defined(__hpux)) && BSD <= 43
1486 static void
1487 rmut(void)
1488 {
1489     int f;
1490     int found = 0;
1491     struct utmp *u, *utmp;
1492     int nutmp;
1493     struct stat statbf;
1494     char *clean_tty = clean_ttyname(line);
1495
1496     f = open(utmpf, O_RDWR);
1497     if (f >= 0) {
1498         fstat(f, &statbf);
1499         utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
1500         if (!utmp)
1501             syslog(LOG_ERR, "utmp malloc failed");
1502         if (statbf.st_size && utmp) {
1503             nutmp = read(f, utmp, (int)statbf.st_size);
1504             nutmp /= sizeof(struct utmp);
1505
1506             for (u = utmp ; u < &utmp[nutmp] ; u++) {
1507                 if (strncmp(u->ut_line,
1508                             clean_tty,
1509                             sizeof(u->ut_line)) ||
1510                     u->ut_name[0]==0)
1511                     continue;
1512                 lseek(f, ((long)u)-((long)utmp), L_SET);
1513                 strncpy(u->ut_name,  "", sizeof(u->ut_name));
1514 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1515                 strncpy(u->ut_host,  "", sizeof(u->ut_host));
1516 #endif
1517                 u->ut_time = time(NULL);
1518                 write(f, u, sizeof(wtmp));
1519                 found++;
1520             }
1521         }
1522         close(f);
1523     }
1524     if (found) {
1525         f = open(wtmpf, O_WRONLY|O_APPEND);
1526         if (f >= 0) {
1527             strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1528             strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1529 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1530             strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1531 #endif
1532             wtmp.ut_time = time(NULL);
1533             write(f, &wtmp, sizeof(wtmp));
1534             close(f);
1535         }
1536     }
1537     chmod(line, 0666);
1538     chown(line, 0, 0);
1539     line[strlen("/dev/")] = 'p';
1540     chmod(line, 0666);
1541     chown(line, 0, 0);
1542 }  /* end of rmut */
1543 #endif  /* CRAY */
1544
1545 #if defined(__hpux) && !defined(HAVE_UTMPX_H)
1546 static void
1547 rmut (char *line)
1548 {
1549     struct utmp utmp;
1550     struct utmp *utptr;
1551     int fd;                     /* for /etc/wtmp */
1552
1553     utmp.ut_type = USER_PROCESS;
1554     strncpy(utmp.ut_line, clean_ttyname(line), sizeof(utmp.ut_line));
1555     setutent();
1556     utptr = getutline(&utmp);
1557     /* write it out only if it exists */
1558     if (utptr) {
1559         utptr->ut_type = DEAD_PROCESS;
1560         utptr->ut_time = time(NULL);
1561         pututline(utptr);
1562         /* set wtmp entry if wtmp file exists */
1563         if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) {
1564             write(fd, utptr, sizeof(utmp));
1565             close(fd);
1566         }
1567     }
1568     endutent();
1569
1570     chmod(line, 0666);
1571     chown(line, 0, 0);
1572     line[14] = line[13];
1573     line[13] = line[12];
1574     line[8] = 'm';
1575     line[9] = '/';
1576     line[10] = 'p';
1577     line[11] = 't';
1578     line[12] = 'y';
1579     chmod(line, 0666);
1580     chown(line, 0, 0);
1581 }
1582 #endif
1583
1584 /*
1585  * cleanup()
1586  *
1587  * This is the routine to call when we are all through, to
1588  * clean up anything that needs to be cleaned up.
1589  */
1590
1591 #ifdef PARENT_DOES_UTMP
1592
1593 void
1594 cleanup(int sig)
1595 {
1596 #ifdef _CRAY
1597     static int incleanup = 0;
1598     int t;
1599     int child_status; /* status of child process as returned by waitpid */
1600     int flags = WNOHANG|WUNTRACED;
1601     
1602     /*
1603      * 1: Pick up the zombie, if we are being called
1604      *    as the signal handler.
1605      * 2: If we are a nested cleanup(), return.
1606      * 3: Try to clean up TMPDIR.
1607      * 4: Fill in utmp with shutdown of process.
1608      * 5: Close down the network and pty connections.
1609      * 6: Finish up the TMPDIR cleanup, if needed.
1610      */
1611     if (sig == SIGCHLD) {
1612         while (waitpid(-1, &child_status, flags) > 0)
1613             ;   /* VOID */
1614         /* Check if the child process was stopped
1615          * rather than exited.  We want cleanup only if
1616          * the child has died.
1617          */
1618         if (WIFSTOPPED(child_status)) {
1619             return;
1620         }
1621     }
1622     t = sigblock(sigmask(SIGCHLD));
1623     if (incleanup) {
1624         sigsetmask(t);
1625         return;
1626     }
1627     incleanup = 1;
1628     sigsetmask(t);
1629     
1630     t = cleantmp(&wtmp);
1631     setutent(); /* just to make sure */
1632 #endif /* CRAY */
1633     rmut(line);
1634     close(ourpty);
1635     shutdown(net, 2);
1636 #ifdef _CRAY
1637     if (t == 0)
1638         cleantmp(&wtmp);
1639 #endif /* CRAY */
1640     exit(1);
1641 }
1642
1643 #else /* PARENT_DOES_UTMP */
1644
1645 void
1646 cleanup(int sig)
1647 {
1648 #if defined(HAVE_UTMPX_H) || !defined(HAVE_LOGWTMP)
1649     rmut();
1650 #ifdef HAVE_VHANGUP
1651 #ifndef __sgi
1652     vhangup(); /* XXX */
1653 #endif
1654 #endif
1655 #else
1656     char *p;
1657     
1658     p = line + sizeof("/dev/") - 1;
1659     if (logout(p))
1660         logwtmp(p, "", "");
1661     chmod(line, 0666);
1662     chown(line, 0, 0);
1663     *p = 'p';
1664     chmod(line, 0666);
1665     chown(line, 0, 0);
1666 #endif
1667     shutdown(net, 2);
1668     exit(1);
1669 }
1670
1671 #endif /* PARENT_DOES_UTMP */
1672
1673 #ifdef PARENT_DOES_UTMP
1674 /*
1675  * _utmp_sig_rcv
1676  * utmp_sig_init
1677  * utmp_sig_wait
1678  *      These three functions are used to coordinate the handling of
1679  *      the utmp file between the server and the soon-to-be-login shell.
1680  *      The server actually creates the utmp structure, the child calls
1681  *      utmp_sig_wait(), until the server calls utmp_sig_notify() and
1682  *      signals the future-login shell to proceed.
1683  */
1684 static int caught=0;            /* NZ when signal intercepted */
1685 static void (*func)();          /* address of previous handler */
1686
1687 void
1688 _utmp_sig_rcv(sig)
1689      int sig;
1690 {
1691     caught = 1;
1692     signal(SIGUSR1, func);
1693 }
1694
1695 void
1696 utmp_sig_init()
1697 {
1698     /*
1699      * register signal handler for UTMP creation
1700      */
1701     if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
1702         fatalperror(net, "telnetd/signal");
1703 }
1704
1705 void
1706 utmp_sig_reset()
1707 {
1708     signal(SIGUSR1, func);      /* reset handler to default */
1709 }
1710
1711 # ifdef __hpux
1712 # define sigoff() /* do nothing */
1713 # define sigon() /* do nothing */
1714 # endif
1715
1716 void
1717 utmp_sig_wait()
1718 {
1719     /*
1720      * Wait for parent to write our utmp entry.
1721          */
1722     sigoff();
1723     while (caught == 0) {
1724         pause();        /* wait until we get a signal (sigon) */
1725         sigoff();       /* turn off signals while we check caught */
1726     }
1727     sigon();            /* turn on signals again */
1728 }
1729
1730 void
1731 utmp_sig_notify(pid)
1732 {
1733     kill(pid, SIGUSR1);
1734 }
1735
1736 #ifdef _CRAY
1737 static int gotsigjob = 0;
1738
1739         /*ARGSUSED*/
1740 void
1741 sigjob(sig)
1742      int sig;
1743 {
1744     int jid;
1745     struct jobtemp *jp;
1746
1747     while ((jid = waitjob(NULL)) != -1) {
1748         if (jid == 0) {
1749             return;
1750         }
1751         gotsigjob++;
1752         jobend(jid, NULL, NULL);
1753     }
1754 }
1755
1756 /*
1757  *      jid_getutid:
1758  *              called by jobend() before calling cleantmp()
1759  *              to find the correct $TMPDIR to cleanup.
1760  */
1761
1762 struct utmp *
1763 jid_getutid(jid)
1764      int jid;
1765 {
1766     struct utmp *cur = NULL;
1767
1768     setutent(); /* just to make sure */
1769     while (cur = getutent()) {
1770         if ( (cur->ut_type != NULL) && (jid == cur->ut_jid) ) {
1771             return(cur);
1772         }
1773     }
1774
1775     return(0);
1776 }
1777
1778 /*
1779  * Clean up the TMPDIR that login created.
1780  * The first time this is called we pick up the info
1781  * from the utmp.  If the job has already gone away,
1782  * then we'll clean up and be done.  If not, then
1783  * when this is called the second time it will wait
1784  * for the signal that the job is done.
1785  */
1786 int
1787 cleantmp(wtp)
1788      struct utmp *wtp;
1789 {
1790     struct utmp *utp;
1791     static int first = 1;
1792     int mask, omask, ret;
1793     extern struct utmp *getutid (const struct utmp *_Id);
1794
1795
1796     mask = sigmask(WJSIGNAL);
1797
1798     if (first == 0) {
1799         omask = sigblock(mask);
1800         while (gotsigjob == 0)
1801             sigpause(omask);
1802         return(1);
1803     }
1804     first = 0;
1805     setutent(); /* just to make sure */
1806
1807     utp = getutid(wtp);
1808     if (utp == 0) {
1809         syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1810         return(-1);
1811     }
1812     /*
1813      * Nothing to clean up if the user shell was never started.
1814      */
1815     if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0)
1816         return(1);
1817
1818     /*
1819      * Block the WJSIGNAL while we are in jobend().
1820      */
1821     omask = sigblock(mask);
1822     ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user);
1823     sigsetmask(omask);
1824     return(ret);
1825 }
1826
1827 int
1828 jobend(jid, path, user)
1829      int jid;
1830      char *path;
1831      char *user;
1832 {
1833     static int saved_jid = 0;
1834     static int pty_saved_jid = 0;
1835     static char saved_path[sizeof(wtmp.ut_tpath)+1];
1836     static char saved_user[sizeof(wtmp.ut_user)+1];
1837
1838     /*
1839      * this little piece of code comes into play
1840      * only when ptyreconnect is used to reconnect
1841      * to an previous session.
1842      *
1843      * this is the only time when the
1844      * "saved_jid != jid" code is executed.
1845      */
1846
1847     if ( saved_jid && saved_jid != jid ) {
1848         if (!path) {    /* called from signal handler */
1849             pty_saved_jid = jid;
1850         } else {
1851             pty_saved_jid = saved_jid;
1852         }
1853     }
1854
1855     if (path) {
1856         strncpy(saved_path, path, sizeof(wtmp.ut_tpath));
1857         strncpy(saved_user, user, sizeof(wtmp.ut_user));
1858         saved_path[sizeof(saved_path)] = '\0';
1859         saved_user[sizeof(saved_user)] = '\0';
1860     }
1861     if (saved_jid == 0) {
1862         saved_jid = jid;
1863         return(0);
1864     }
1865
1866     /* if the jid has changed, get the correct entry from the utmp file */
1867
1868     if ( saved_jid != jid ) {
1869         struct utmp *utp = NULL;
1870         struct utmp *jid_getutid();
1871
1872         utp = jid_getutid(pty_saved_jid);
1873
1874         if (utp == 0) {
1875             syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1876             return(-1);
1877         }
1878
1879         cleantmpdir(jid, utp->ut_tpath, utp->ut_user);
1880         return(1);
1881     }
1882
1883     cleantmpdir(jid, saved_path, saved_user);
1884     return(1);
1885 }
1886
1887 /*
1888  * Fork a child process to clean up the TMPDIR
1889  */
1890 cleantmpdir(jid, tpath, user)
1891      int jid;
1892      char *tpath;
1893      char *user;
1894 {
1895     switch(fork()) {
1896     case -1:
1897         syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n",
1898                tpath);
1899         break;
1900     case 0:
1901         execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
1902         syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n",
1903                tpath, CLEANTMPCMD);
1904         exit(1);
1905     default:
1906         /*
1907          * Forget about child.  We will exit, and
1908          * /etc/init will pick it up.
1909          */
1910         break;
1911     }
1912 }
1913 #endif /* CRAY */
1914 #endif  /* defined(PARENT_DOES_UTMP) */