FreeBSD-SA-09:05.telnet - fix environment based code execution vulnerability
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / telnet / telnetd / sys_term.c
... / ...
CommitLineData
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
36RCSID("$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
51struct utmpx wtmp;
52#elif defined(HAVE_UTMP_H)
53struct utmp wtmp;
54#endif /* HAVE_UTMPX_H */
55
56#ifdef HAVE_STRUCT_UTMP_UT_HOST
57int utmp_len = sizeof(wtmp.ut_host);
58#else
59int 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
76char wtmpf[] = WTMP_FILE;
77#else
78char wtmpf[] = "/usr/adm/wtmp";
79#endif
80char utmpf[] = UTMP_FILE;
81#else /* PARENT_DOES_UTMP */
82#ifdef WTMP_FILE
83char wtmpf[] = WTMP_FILE;
84#else
85char 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
212void
213set_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
240int
241spcset(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 */
338int
339getnpty()
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
361static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
362char *line = Xline;
363
364#ifdef _CRAY
365char 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)
369static char *ptsname(int fd)
370{
371#ifdef HAVE_TTYNAME
372 return ttyname(fd);
373#else
374 return NULL;
375#endif
376}
377#endif
378
379int 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
528int
529tty_isecho(void)
530{
531 return (termbuf.c_lflag & ECHO);
532}
533
534int
535tty_flowmode(void)
536{
537 return((termbuf.c_iflag & IXON) ? 1 : 0);
538}
539
540int
541tty_restartany(void)
542{
543 return((termbuf.c_iflag & IXANY) ? 1 : 0);
544}
545
546void
547tty_setecho(int on)
548{
549 if (on)
550 termbuf.c_lflag |= ECHO;
551 else
552 termbuf.c_lflag &= ~ECHO;
553}
554
555int
556tty_israw(void)
557{
558 return(!(termbuf.c_lflag & ICANON));
559}
560
561void
562tty_binaryin(int on)
563{
564 if (on) {
565 termbuf.c_iflag &= ~ISTRIP;
566 } else {
567 termbuf.c_iflag |= ISTRIP;
568 }
569}
570
571void
572tty_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
585int
586tty_isbinaryin(void)
587{
588 return(!(termbuf.c_iflag & ISTRIP));
589}
590
591int
592tty_isbinaryout(void)
593{
594 return(!(termbuf.c_oflag&OPOST));
595}
596
597
598int
599tty_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
609void
610tty_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
631int
632tty_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
645void
646tty_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
662int
663tty_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 */
680struct 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
718void
719tty_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
734void
735tty_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
751extern struct utmp wtmp;
752extern char wtmpf[];
753
754extern void utmp_sig_init (void);
755extern void utmp_sig_reset (void);
756extern void utmp_sig_wait (void);
757extern 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 */
763static 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
794static 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 */
825void 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
965int 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
1025int 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
1075static char *
1076clean_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
1094static char *
1095make_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 */
1115void
1116startslave(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
1193char *envinit[3];
1194extern char **environ;
1195
1196void
1197init_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
1218static void
1219scrub_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
1277struct arg_val {
1278 int size;
1279 int argc;
1280 const char **argv;
1281};
1282
1283static 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
1292void
1293start_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
1399static void
1400addarg(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
1421static void
1422rmut(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
1486static void
1487rmut(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)
1546static void
1547rmut (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
1593void
1594cleanup(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
1645void
1646cleanup(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 */
1684static int caught=0; /* NZ when signal intercepted */
1685static void (*func)(); /* address of previous handler */
1686
1687void
1688_utmp_sig_rcv(sig)
1689 int sig;
1690{
1691 caught = 1;
1692 signal(SIGUSR1, func);
1693}
1694
1695void
1696utmp_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
1705void
1706utmp_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
1716void
1717utmp_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
1730void
1731utmp_sig_notify(pid)
1732{
1733 kill(pid, SIGUSR1);
1734}
1735
1736#ifdef _CRAY
1737static int gotsigjob = 0;
1738
1739 /*ARGSUSED*/
1740void
1741sigjob(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
1762struct utmp *
1763jid_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 */
1786int
1787cleantmp(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
1827int
1828jobend(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 */
1890cleantmpdir(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) */