|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)telnetd.c 4.26 (Berkeley) 83/08/06";
3: #endif
4:
5: /*
6: * Stripped-down telnet server.
7: */
8: #include <stdio.h>
9: #include <sys/param.h>
10: #include <sys/types.h>
11: #include <sys/stat.h>
12:
13: #include <errno.h>
14: #include <pwd.h>
15: #include <signal.h>
16: #include <sgtty.h>
17: #include <stdio.h>
18: #include <wait.h>
19: #include <sys/ttyld.h>
20: #include <sys/stream.h>
21: #include "telnet.h"
22:
23: #define BELL '\07'
24: #define DEBUG if (debug)
25:
26: /* option settings (remote and local) */
27: static char hisopts[256];
28: static char myopts[256];
29:
30: /* formats for option messages */
31: static char doopt[] = { IAC, DO, '%', 'c', 0 };
32: static char dont[] = { IAC, DONT, '%', 'c', 0 };
33: static char will[] = { IAC, WILL, '%', 'c', 0 };
34: static char wont[] = { IAC, WONT, '%', 'c', 0 };
35:
36: static int ptfd, netfd; /* fd's to pt and tcp */
37: static int done; /* true if session is to be ended */
38: static int debug; /* true if debugging is to be output */
39: static struct sgttyb ptb; /* result of an IOCGETP on the pt */
40: static struct tchars ptt; /* result of an IOCGETC on the pt */
41:
42: /* predefined */
43: static reapchild();
44: static catchint();
45:
46: /* imported */
47: extern char **environ;
48: extern int errno;
49: extern char *strrchr(), *strchr();
50:
51:
52: /*
53: * The following macros and routines are used to
54: * manage the I/O buffers. They're a cross between
55: * stream buffers and standard I/O.
56: */
57: struct buffer {
58: char *b_rp; /* read pointer */
59: char *b_wp; /* write pointer */
60: char b_buf[BUFSIZ]; /* the buffer */
61: };
62: struct buffer ptibuf, *ptin = &ptibuf;
63: struct buffer ptobuf, *ptout = &ptobuf;
64: struct buffer netibuf, *netin = &netibuf;
65: struct buffer netobuf, *netout = &netobuf;
66:
67: #define binit(bp) (bp->b_rp = bp->b_wp = bp->b_buf)
68: #define bytes_filled(bp) (bp->b_wp - bp->b_rp)
69: #define space_left(bp) (bp->b_buf+sizeof(bp->b_buf) - bp->b_wp)
70: #define bput(bp, c) (*(bp->b_wp++) = c)
71: #define bget(bp) (*(bp->b_rp++) & 0377)
72:
73: /* read whatever the buffer can take */
74: static int
75: bread(bp, fd)
76: struct buffer *bp;
77: int fd;
78: {
79: int cc;
80:
81: /* normalize the buffer */
82: if (bytes_filled(bp) == 0)
83: bp->b_rp = bp->b_wp = bp->b_buf;
84:
85: /* fill it */
86: cc = read(fd, bp->b_wp, space_left(bp));
87: if (cc > 0)
88: bp->b_wp += cc;
89: return cc;
90: }
91:
92: /* read at most n bytes */
93: static int
94: breadn(bp, fd, n)
95: struct buffer *bp;
96: int fd, n;
97: {
98: int cc;
99:
100: /* normalize the buffer */
101: if (bytes_filled(bp) == 0)
102: bp->b_rp = bp->b_wp = bp->b_buf;
103: if (n > space_left(bp))
104: n = space_left(bp);
105:
106: /* fill it */
107: cc = read(fd, bp->b_wp, n);
108: if (cc > 0)
109: bp->b_wp += cc;
110: return cc;
111: }
112:
113: /* empty the buffer */
114: static int
115: bwrite(bp, fd)
116: struct buffer *bp;
117: int fd;
118: {
119: int cc;
120:
121:
122: cc = write(fd, bp->b_rp, bytes_filled(bp));
123:
124: /* normalize the buffer */
125: if (cc == bytes_filled(bp))
126: binit(bp);
127: return cc;
128: }
129: static int
130: bputs(bp, s)
131: struct buffer *bp;
132: char *s;
133: {
134: while (*s)
135: bput(bp, *s++);
136: }
137:
138: /*
139: * Establish a tcp socket and fork off a process for each connection.
140: */
141: main(argc, argv)
142: char *argv[];
143: {
144: int finish();
145: int f;
146:
147: doit(0);
148: rmut();
149: /*NOTREACHED*/
150: }
151:
152: /*
153: * Get a pt. Put a login on one side and a telnet receiver on
154: * the other.
155: */
156: static
157: doit(f)
158: int f;
159: {
160: int pfd[2];
161: # define TERMEND pfd[0]
162: # define PROCEND pfd[1]
163: extern int tty_ld, mesg_ld;
164:
165: /* get a pt pair */
166: if (pipe(pfd) < 0)
167: fatalperror(f, "out of pipes", errno);
168:
169: /* make it really look like a terminal */
170: if (ioctl(TERMEND, FIOPUSHLD, (struct sgttyb *)&mesg_ld)<0)
171: fatalperror(f, "can't push mesgld", errno);
172: if (ioctl(PROCEND, FIOPUSHLD, (struct sgttyb *)&tty_ld)<0)
173: fatalperror(f, "can't push ttyld", errno);
174:
175: /* prepare for the death of a child */
176: signal(SIGCHLD, catchint);
177: signal(SIGHUP, catchint);
178: signal(SIGPIPE, catchint);
179: switch (fork()) {
180: case -1:
181: fatalperror(f, "fork", errno);
182: case 0:
183: /* a process which is the remote login */
184: getty(PROCEND);
185: default:
186: /* the protocol process */
187: close(PROCEND);
188: netfd = f;
189: ptfd = TERMEND;
190: telnet();
191: }
192: /*NOTREACHED*/
193: }
194:
195: static
196: fatal(f, msg)
197: int f;
198: char *msg;
199: {
200: char buf[BUFSIZ];
201:
202: (void) sprintf(buf, "telnetd: %s.\n", msg);
203: (void) write(f, buf, strlen(buf));
204: rmut();
205: exit(1);
206: }
207: static
208: finish()
209: {
210: if (debug) fprintf(stderr, "pipe closed\n");
211: rmut();
212: exit(1);
213: }
214:
215: static
216: fatalperror(f, msg, errno)
217: int f;
218: char *msg;
219: int errno;
220: {
221: char buf[BUFSIZ];
222: extern char *sys_errlist[];
223:
224: (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
225: fatal(f, buf);
226: }
227:
228: static
229: terminate(s)
230: char *s;
231: {
232: DEBUG fprintf(stderr, "session on terminated because of %s\n", s);
233: done = 1;
234: }
235:
236: /* loop on input from the pt and the network */
237: static
238: telnet()
239: {
240: int n;
241: fd_set ibits, obits;
242:
243: binit(ptin);
244: binit(ptout);
245: binit(netin);
246: binit(netout);
247:
248: /* Request to do remote echo. */
249: dooption(TELOPT_ECHO);
250: myopts[TELOPT_ECHO] = 1;
251:
252: while(!done) {
253: FD_ZERO(ibits);
254: FD_ZERO(obits);
255:
256: /* process anything sitting in the input buffers */
257: if (bytes_filled(ptin) > 0)
258: ptprocess();
259: if (bytes_filled(netin) > 0)
260: netprocess();
261:
262: /* select for read only if there's room to read into */
263: if (space_left(netin) > 0)
264: FD_SET(netfd, ibits);
265: if (space_left(ptin) > 0)
266: FD_SET(ptfd, ibits);
267:
268: /* select for write only if there's something to write */
269: if (bytes_filled(ptout) > 0)
270: FD_SET(ptfd, obits);
271: if (bytes_filled(netout) > 0)
272: FD_SET(netfd, obits);
273:
274: n = select(NOFILE, &ibits, &obits, 100000);
275: if (n < 0)
276: break;
277: else if (n == 0)
278: continue;
279:
280: /* fill input buffers */
281: if (FD_ISSET(netfd, ibits))
282: netrcv();
283: if (FD_ISSET(ptfd, ibits))
284: ptrcv();
285:
286: /* flush output buffers */
287: if (FD_ISSET(netfd, obits) && bytes_filled(netout) > 0)
288: netflush();
289: if (FD_ISSET(ptfd, obits) && bytes_filled(ptout) > 0)
290: ptflush();
291: }
292: rmut();
293: exit(0);
294: }
295:
296:
297: /* read from the pt */
298: static
299: ptrcv()
300: {
301: static struct mesg m;
302: static int size = 0;
303:
304: /* get the header if we don't already have one */
305: if (size <= 0) {
306: if (read(ptfd, (char *)&m, MSGHLEN) != MSGHLEN) {
307: terminate("pt read");
308: return;
309: }
310: size = (m.losize & 0377) + ((m.hisize & 0377) << 8);
311: }
312: switch(m.type) {
313: case M_HANGUP:
314: terminate("pt hangup");
315: size=0;
316: break;
317: case M_DATA:
318: size=datamesg(size);
319: break;
320: case M_IOCTL:
321: ioctlmesg(size);
322: size=0;
323: break;
324: case M_IOCACK:
325: fprintf(stderr, "IOCACK\n");
326: flushmesg(size);
327: size=0;
328: break;
329: case M_IOCNAK:
330: fprintf(stderr, "IOCNAK\n");
331: flushmesg(size);
332: size=0;
333: break;
334: default:
335: othermesg(m, size);
336: size=0;
337: break;
338: }
339: }
340:
341: /* flush a message from the pt */
342: static
343: flushmesg(size)
344: {
345: char buf[64];
346: int cc;
347: /* flush it */
348: while (size > 0) {
349: fprintf(stderr, "flush\n");
350: cc = size > sizeof(buf) ? sizeof(buf) : size;
351: cc = read(ptfd, buf, cc);
352: if (cc < 0) {
353: terminate("pt read");
354: return;
355: }
356: size -= cc;
357: }
358: }
359:
360: /* handle an ioctl message from the pt */
361: static
362: ioctlmesg(size)
363: {
364: struct mesg rm;
365: union stmsg s;
366: int cc;
367:
368: if (size > 0) {
369: cc = read(ptfd, (char *)&s, size > sizeof(s) ? sizeof(s) : size);
370: if (cc < 0) {
371: terminate("pt read");
372: return;
373: }
374: if (size > cc)
375: flushmesg(size - cc);
376: }
377: rm.type = M_IOCACK;
378: switch (s.ioc0.com) {
379:
380: case TIOCSETN:
381: case TIOCSETP:
382: size = 0;
383: break;
384:
385: case TIOCGETP:
386: s.ioc1.sb.sg_ispeed = B9600;
387: s.ioc1.sb.sg_ospeed = B9600;
388: ptb = s.ioc1.sb; /* sic; remember what ttyld said */
389: /* leave size as it was */
390: break;
391:
392: default:
393: rm.type = M_IOCNAK;
394: size = 0;
395: break;
396: }
397: rm.magic = MSGMAGIC;
398: rm.losize = size;
399: rm.hisize = size>>8;
400: if (write(ptfd, (char *)&rm, MSGHLEN) != MSGHLEN) {
401: terminate("pt write");
402: return;
403: }
404: if (size > 0)
405: if (write(ptfd, (char *)&s, size) != size) {
406: terminate("pt write");
407: return;
408: }
409: }
410:
411: /* read bytes from the pt and write to the network connection */
412: static
413: datamesg(size)
414: {
415: int cc;
416:
417: cc = breadn(ptin, ptfd, size);
418: if (cc < 0)
419: terminate("pt read");
420: else
421: size -= cc;
422: return (size);
423: }
424:
425: /* handle an unrecognized type of message */
426: static
427: othermesg(m, size)
428: struct mesg m;
429: {
430: char buf[132];
431: int rcc, wcc;
432:
433: wcc = write(ptfd, (char *)&m, MSGHLEN);
434: if (wcc != MSGHLEN) {
435: terminate("pt write");
436: return;
437: }
438: while (size > 0) {
439: rcc = read(ptfd, buf, size > sizeof(buf) ? sizeof(buf) : size);
440: if (rcc <= 0) {
441: terminate("pt read");
442: return;
443: }
444: wcc = write(ptfd, buf, rcc);
445: if (wcc != rcc) {
446: terminate("pt write");
447: return;
448: }
449: size -= rcc;
450: }
451: }
452:
453: /* set pt mode */
454: static
455: mode(on, off)
456: int on, off;
457: {
458: ptflush();
459: ptb.sg_flags |= on;
460: ptb.sg_flags &= ~off;
461: if(ioctl(ptfd, TIOCSETP, &ptb)<0)
462: terminate("setting mode of pt");
463: }
464:
465: /* flush the pt's output buffer */
466: ptflush()
467: {
468: struct mesg rm;
469: int size;
470:
471: if ((size = bytes_filled(ptout)) > 0) {
472: rm.type = M_DATA;
473: rm.magic = MSGMAGIC;
474: rm.losize = size;
475: rm.hisize = size>>8;
476: if (write(ptfd, (char *)&rm, MSGHLEN) < MSGHLEN)
477: terminate("pt write");
478: else if (bwrite(ptout, ptfd) <= 0)
479: terminate("pt write");
480: }
481: }
482:
483: /* process bytes from the pt */
484: static
485: ptprocess()
486: {
487: register int c;
488:
489: while(bytes_filled(ptin) && space_left(netout) > 1) {
490: c = bget(ptin);
491: if (c == IAC)
492: bput(netout, c);
493: bput(netout, c);
494: }
495: }
496:
497: /* read bytes from the net */
498: static
499: netrcv()
500: {
501: if (bread(netin, netfd) < 0)
502: terminate("net read");
503: }
504:
505: /* flush the netwrk's output buffer */
506: netflush()
507: {
508: if (bwrite(netout, netfd) < 0)
509: terminate("net write");
510: }
511:
512: /*
513: * State for recv fsm
514: */
515: #define TS_DATA 0 /* base state */
516: #define TS_IAC 1 /* look for double IAC's */
517: #define TS_CR 2 /* CR-LF ->'s CR */
518: #define TS_BEGINNEG 3 /* throw away begin's... */
519: #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */
520: #define TS_WILL 5 /* will option negotiation */
521: #define TS_WONT 6 /* wont " */
522: #define TS_DO 7 /* do " */
523: #define TS_DONT 8 /* dont " */
524:
525: static
526: netprocess()
527: {
528: static int state = TS_DATA;
529: register int c;
530: char buf[10];
531:
532: while (bytes_filled(netin)) {
533: c = bget(netin);
534: switch (state) {
535:
536: case TS_DATA:
537: if (c == IAC) {
538: state = TS_IAC;
539: break;
540: }
541: bput(ptout, c);
542: if (!myopts[TELOPT_BINARY] && c == '\r')
543: state = TS_CR;
544: break;
545:
546: case TS_CR:
547: if (c && c != '\n')
548: bput(ptout, c);
549: state = TS_DATA;
550: break;
551:
552: case TS_IAC:
553: switch (c) {
554:
555: /*
556: * Send the process on the pty side an
557: * interrupt. Do this with a NULL or
558: * interrupt char; depending on the tty mode.
559: */
560: case BREAK:
561: case IP:
562: interrupt();
563: break;
564:
565: /*
566: * Are You There?
567: */
568: case AYT:
569: bput(ptout, BELL);
570: break;
571:
572: /*
573: * Erase Character and
574: * Erase Line
575: */
576: case EC:
577: case EL:
578: ptflush();
579: bput(ptout, (c == EC) ? ptb.sg_erase : ptb.sg_kill);
580: break;
581:
582: /*
583: * Check for urgent data...
584: */
585: case DM:
586: break;
587:
588: /*
589: * Begin option subnegotiation...
590: */
591: case SB:
592: state = TS_BEGINNEG;
593: continue;
594:
595: case WILL:
596: case WONT:
597: case DO:
598: case DONT:
599: state = TS_WILL + (c - WILL);
600: continue;
601:
602: case IAC:
603: bput(ptout, c);
604: break;
605: }
606: state = TS_DATA;
607: break;
608:
609: case TS_BEGINNEG:
610: if (c == IAC)
611: state = TS_ENDNEG;
612: break;
613:
614: case TS_ENDNEG:
615: state = c == SE ? TS_DATA : TS_BEGINNEG;
616: break;
617:
618: case TS_WILL:
619: if (!hisopts[c])
620: willoption(c);
621: state = TS_DATA;
622: continue;
623:
624: case TS_WONT:
625: if (hisopts[c])
626: wontoption(c);
627: state = TS_DATA;
628: continue;
629:
630: case TS_DO:
631: if (!myopts[c])
632: dooption(c);
633: state = TS_DATA;
634: continue;
635:
636: case TS_DONT:
637: if (myopts[c]) {
638: myopts[c] = 0;
639: sprintf(buf, wont, c);
640: bputs(netout, buf);
641: }
642: state = TS_DATA;
643: continue;
644:
645: default:
646: printf("telnetd: panic state=%d\n", state);
647: exit(1);
648: }
649: }
650: }
651:
652: static
653: willoption(option)
654: int option;
655: {
656: char *fmt;
657: char buf[10];
658:
659: switch (option) {
660:
661: case TELOPT_BINARY:
662: mode(RAW, 0);
663: goto common;
664:
665: case TELOPT_ECHO:
666: mode(0, ECHO|CRMOD);
667: /*FALL THRU*/
668:
669: case TELOPT_SGA:
670: common:
671: hisopts[option] = 1;
672: fmt = doopt;
673: break;
674:
675: case TELOPT_TM:
676: fmt = dont;
677: break;
678:
679: default:
680: fmt = dont;
681: break;
682: }
683: sprintf(buf, fmt, option);
684: bputs(netout, buf);
685: }
686:
687: static
688: wontoption(option)
689: int option;
690: {
691: char *fmt;
692: char buf[10];
693:
694: switch (option) {
695:
696: case TELOPT_ECHO:
697: mode(ECHO|CRMOD, 0);
698: goto common;
699:
700: case TELOPT_BINARY:
701: mode(0, RAW);
702: /*FALL THRU*/
703:
704: case TELOPT_SGA:
705: common:
706: hisopts[option] = 0;
707: fmt = dont;
708: break;
709:
710: default:
711: fmt = dont;
712: }
713: sprintf(buf, fmt, option);
714: bputs(netout, buf);
715: }
716:
717: static
718: dooption(option)
719: int option;
720: {
721: char *fmt;
722: char buf[10];
723:
724: switch (option) {
725:
726: case TELOPT_TM:
727: fmt = wont;
728: break;
729:
730: case TELOPT_ECHO:
731: mode(ECHO|CRMOD, 0);
732: goto common;
733:
734: case TELOPT_BINARY:
735: mode(RAW, 0);
736: /*FALL THRU*/
737:
738: case TELOPT_SGA:
739: common:
740: fmt = will;
741: break;
742:
743: default:
744: fmt = wont;
745: break;
746: }
747: sprintf(buf, fmt, option);
748: bputs(netout, buf);
749: }
750:
751: /*
752: * Send interrupt to process on other side of pty.
753: * If it is in raw mode, just write NULL;
754: * otherwise, write intr char.
755: */
756: static
757: interrupt()
758: {
759: ptflush();
760: ioctl(ptfd, TIOCGETC, &ptt);
761: bput(ptout, ptb.sg_flags & RAW ? '\0' : ptt.t_intrc);
762: }
763:
764: static
765: catchint()
766: {
767: terminate("child death");
768: }
769:
770: #include <utmp.h>
771:
772: struct utmp wtmp;
773: char wtmpf[] = "/usr/adm/wtmp";
774: char utmp[] = "/etc/utmp";
775: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
776: #define SCMPN(a, b) strncmp(a, b, sizeof (a))
777:
778: static
779: rmut()
780: {
781: register f;
782: int found = 0;
783: char *line, *dev;
784: long time();
785:
786: dev = NULL;
787: if (dev == NULL)
788: return;
789: line = dev + 5;
790:
791: DEBUG fprintf(stderr, "closing connection on %s\n", line);
792:
793: f = open(utmp, 2);
794: if (f >= 0) {
795: while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
796: if (SCMPN(wtmp.ut_line, line) || wtmp.ut_name[0]==0)
797: continue;
798: lseek(f, -(long)sizeof (wtmp), 1);
799: SCPYN(wtmp.ut_name, "");
800: time(&wtmp.ut_time);
801: write(f, (char *)&wtmp, sizeof (wtmp));
802: found++;
803: }
804: close(f);
805: }
806: if (found) {
807: f = open(wtmpf, 1);
808: if (f >= 0) {
809: SCPYN(wtmp.ut_line, line);
810: SCPYN(wtmp.ut_name, "");
811: time(&wtmp.ut_time);
812: lseek(f, (long)0, 2);
813: write(f, (char *)&wtmp, sizeof (wtmp));
814: close(f);
815: }
816: }
817: chown(dev, 0, 0);
818: chmod(dev, 0666);
819: }
820:
821:
822: static
823: getty(f)
824: {
825: int i;
826: struct sgttyb b;
827: char banner[128];
828: char *envp[4];
829: char **p, **passenv();
830: char *whoami();
831:
832: ioctl(f, TIOCSPGRP, 0);
833: signal(SIGTERM, SIG_DFL) ;
834: signal(SIGPIPE, SIG_DFL) ;
835: signal(SIGQUIT, SIG_DFL) ;
836: signal(SIGINT, SIG_DFL) ;
837: signal(SIGALRM, SIG_DFL) ;
838: signal(SIGHUP, SIG_DFL) ;
839: signal(SIGCHLD, SIG_DFL) ;
840: for (i = 0; i < NSYSFILE; i++)
841: if (i != f)
842: close(i);
843: for (i = 0; i < NSYSFILE; i++)
844: dup(f);
845: for (i=NSYSFILE; i<NOFILE; i++)
846: close(i) ;
847: ioctl(0, TIOCGETP, &b);
848: b.sg_flags |= CRMOD|XTABS|ANYP|ECHO;
849: b.sg_erase = '#';
850: b.sg_kill = '@';
851: ioctl(0, TIOCSETP, &b);
852: p = envp;
853: p = passenv(p, "CSOURCE");
854: *p = (char *)0;
855: sprintf(banner, "%s\n", whoami());
856: write (netfd, banner, strlen(banner));
857: execle("/etc/login", "login", 0, envp);
858: execle("/etc/login", "login", 0, envp);
859: fatalperror(2, "/etc/login", errno);
860: exit(1);
861: }
862:
863: char**
864: passenv(ep, var)
865: char **ep;
866: {
867: char *cp;
868: char *getenv();
869:
870: cp = getenv(var);
871: if(cp){
872: *ep = cp-strlen(var)-1;
873: ep++;
874: }
875: return ep;
876: }
877:
878: char *
879: whoami()
880: {
881: static char name[128];
882: int fd, n;
883: char *cp;
884:
885: fd = open("/etc/whoami", 0);
886: if (fd < 0)
887: return ("Kremvax");
888: n = read(fd, name, sizeof(name)-1);
889: if (n <= 0)
890: return ("Kremvax");
891: name[n] = '\0';
892: for (cp=name; *cp; cp++)
893: if (*cp == '\n') {
894: *cp = '\0';
895: break;
896: }
897: return name;
898: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.