1 /* 2 * Copyright (c) 1983 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 char copyright[] = 9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10 All rights reserved.\n"; 11 #endif not lint 12 13 #ifndef lint 14 static char sccsid[] = "@(#)telnetd.c 5.4 (Berkeley) 10/22/85"; 15 #endif not lint 16 17 /* 18 * Stripped-down telnet server. 19 */ 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <sys/wait.h> 23 #include <sys/file.h> 24 #include <sys/stat.h> 25 26 #include <netinet/in.h> 27 28 #include <arpa/telnet.h> 29 30 #include <stdio.h> 31 #include <signal.h> 32 #include <errno.h> 33 #include <sgtty.h> 34 #include <netdb.h> 35 #include <syslog.h> 36 37 #define BELL '\07' 38 #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s" 39 40 char hisopts[256]; 41 char myopts[256]; 42 43 char doopt[] = { IAC, DO, '%', 'c', 0 }; 44 char dont[] = { IAC, DONT, '%', 'c', 0 }; 45 char will[] = { IAC, WILL, '%', 'c', 0 }; 46 char wont[] = { IAC, WONT, '%', 'c', 0 }; 47 48 /* 49 * I/O data buffers, pointers, and counters. 50 */ 51 char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 52 char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 53 char netibuf[BUFSIZ], *netip = netibuf; 54 char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 55 int pcc, ncc; 56 57 int pty, net; 58 int inter; 59 extern char **environ; 60 extern int errno; 61 char *line; 62 63 main(argc, argv) 64 char *argv[]; 65 { 66 struct sockaddr_in from; 67 int on = 1, fromlen; 68 69 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 70 fromlen = sizeof (from); 71 if (getpeername(0, &from, &fromlen) < 0) { 72 fprintf(stderr, "%s: ", argv[0]); 73 perror("getpeername"); 74 _exit(1); 75 } 76 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 77 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 78 } 79 doit(0, &from); 80 } 81 82 char *envinit[] = { "TERM=network", 0 }; 83 int cleanup(); 84 85 /* 86 * Get a pty, scan input lines. 87 */ 88 doit(f, who) 89 int f; 90 struct sockaddr_in *who; 91 { 92 char *host, *inet_ntoa(); 93 int i, p, t; 94 struct sgttyb b; 95 struct hostent *hp; 96 char c; 97 98 for (c = 'p'; c <= 's'; c++) { 99 struct stat stb; 100 101 line = "/dev/ptyXX"; 102 line[strlen("/dev/pty")] = c; 103 line[strlen("/dev/ptyp")] = '0'; 104 if (stat(line, &stb) < 0) 105 break; 106 for (i = 0; i < 16; i++) { 107 line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 108 p = open(line, 2); 109 if (p > 0) 110 goto gotpty; 111 } 112 } 113 fatal(f, "All network ports in use"); 114 /*NOTREACHED*/ 115 gotpty: 116 dup2(f, 0); 117 line[strlen("/dev/")] = 't'; 118 t = open("/dev/tty", O_RDWR); 119 if (t >= 0) { 120 ioctl(t, TIOCNOTTY, 0); 121 close(t); 122 } 123 t = open(line, O_RDWR); 124 if (t < 0) 125 fatalperror(f, line, errno); 126 ioctl(t, TIOCGETP, &b); 127 b.sg_flags = CRMOD|XTABS|ANYP; 128 ioctl(t, TIOCSETP, &b); 129 ioctl(p, TIOCGETP, &b); 130 b.sg_flags &= ~ECHO; 131 ioctl(p, TIOCSETP, &b); 132 hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 133 who->sin_family); 134 if (hp) 135 host = hp->h_name; 136 else 137 host = inet_ntoa(who->sin_addr); 138 if ((i = fork()) < 0) 139 fatalperror(f, "fork", errno); 140 if (i) 141 telnet(f, p); 142 close(f); 143 close(p); 144 dup2(t, 0); 145 dup2(t, 1); 146 dup2(t, 2); 147 close(t); 148 environ = envinit; 149 execl("/bin/login", "login", "-h", host, 0); 150 fatalperror(f, "/bin/login", errno); 151 /*NOTREACHED*/ 152 } 153 154 fatal(f, msg) 155 int f; 156 char *msg; 157 { 158 char buf[BUFSIZ]; 159 160 (void) sprintf(buf, "telnetd: %s.\r\n", msg); 161 (void) write(f, buf, strlen(buf)); 162 exit(1); 163 } 164 165 fatalperror(f, msg, errno) 166 int f; 167 char *msg; 168 int errno; 169 { 170 char buf[BUFSIZ]; 171 extern char *sys_errlist[]; 172 173 (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 174 fatal(f, buf); 175 } 176 177 /* 178 * Main loop. Select from pty and network, and 179 * hand data to telnet receiver finite state machine. 180 */ 181 telnet(f, p) 182 { 183 int on = 1; 184 char hostname[32]; 185 186 net = f, pty = p; 187 ioctl(f, FIONBIO, &on); 188 ioctl(p, FIONBIO, &on); 189 signal(SIGTSTP, SIG_IGN); 190 signal(SIGCHLD, cleanup); 191 192 /* 193 * Request to do remote echo. 194 */ 195 dooption(TELOPT_ECHO); 196 myopts[TELOPT_ECHO] = 1; 197 /* 198 * Show banner that getty never gave. 199 */ 200 gethostname(hostname, sizeof (hostname)); 201 sprintf(nfrontp, BANNER, hostname, ""); 202 nfrontp += strlen(nfrontp); 203 for (;;) { 204 int ibits = 0, obits = 0; 205 register int c; 206 207 /* 208 * Never look for input if there's still 209 * stuff in the corresponding output buffer 210 */ 211 if (nfrontp - nbackp || pcc > 0) 212 obits |= (1 << f); 213 else 214 ibits |= (1 << p); 215 if (pfrontp - pbackp || ncc > 0) 216 obits |= (1 << p); 217 else 218 ibits |= (1 << f); 219 if (ncc < 0 && pcc < 0) 220 break; 221 select(16, &ibits, &obits, 0, 0); 222 if (ibits == 0 && obits == 0) { 223 sleep(5); 224 continue; 225 } 226 227 /* 228 * Something to read from the network... 229 */ 230 if (ibits & (1 << f)) { 231 ncc = read(f, netibuf, BUFSIZ); 232 if (ncc < 0 && errno == EWOULDBLOCK) 233 ncc = 0; 234 else { 235 if (ncc <= 0) 236 break; 237 netip = netibuf; 238 } 239 } 240 241 /* 242 * Something to read from the pty... 243 */ 244 if (ibits & (1 << p)) { 245 pcc = read(p, ptyibuf, BUFSIZ); 246 if (pcc < 0 && errno == EWOULDBLOCK) 247 pcc = 0; 248 else { 249 if (pcc <= 0) 250 break; 251 ptyip = ptyibuf; 252 } 253 } 254 255 while (pcc > 0) { 256 if ((&netobuf[BUFSIZ] - nfrontp) < 2) 257 break; 258 c = *ptyip++ & 0377, pcc--; 259 if (c == IAC) 260 *nfrontp++ = c; 261 *nfrontp++ = c; 262 } 263 if ((obits & (1 << f)) && (nfrontp - nbackp) > 0) 264 netflush(); 265 if (ncc > 0) 266 telrcv(); 267 if ((obits & (1 << p)) && (pfrontp - pbackp) > 0) 268 ptyflush(); 269 } 270 cleanup(); 271 } 272 273 /* 274 * State for recv fsm 275 */ 276 #define TS_DATA 0 /* base state */ 277 #define TS_IAC 1 /* look for double IAC's */ 278 #define TS_CR 2 /* CR-LF ->'s CR */ 279 #define TS_BEGINNEG 3 /* throw away begin's... */ 280 #define TS_ENDNEG 4 /* ...end's (suboption negotiation) */ 281 #define TS_WILL 5 /* will option negotiation */ 282 #define TS_WONT 6 /* wont " */ 283 #define TS_DO 7 /* do " */ 284 #define TS_DONT 8 /* dont " */ 285 286 telrcv() 287 { 288 register int c; 289 static int state = TS_DATA; 290 struct sgttyb b; 291 292 while (ncc > 0) { 293 if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 294 return; 295 c = *netip++ & 0377, ncc--; 296 switch (state) { 297 298 case TS_DATA: 299 if (c == IAC) { 300 state = TS_IAC; 301 break; 302 } 303 if (inter > 0) 304 break; 305 *pfrontp++ = c; 306 if (!myopts[TELOPT_BINARY] && c == '\r') 307 state = TS_CR; 308 break; 309 310 case TS_CR: 311 if (c && c != '\n') 312 *pfrontp++ = c; 313 state = TS_DATA; 314 break; 315 316 case TS_IAC: 317 switch (c) { 318 319 /* 320 * Send the process on the pty side an 321 * interrupt. Do this with a NULL or 322 * interrupt char; depending on the tty mode. 323 */ 324 case BREAK: 325 case IP: 326 interrupt(); 327 break; 328 329 /* 330 * Are You There? 331 */ 332 case AYT: 333 strcpy(nfrontp, "\r\n[Yes]\r\n"); 334 nfrontp += 9; 335 break; 336 337 /* 338 * Erase Character and 339 * Erase Line 340 */ 341 case EC: 342 case EL: 343 ptyflush(); /* half-hearted */ 344 ioctl(pty, TIOCGETP, &b); 345 *pfrontp++ = (c == EC) ? 346 b.sg_erase : b.sg_kill; 347 break; 348 349 /* 350 * Check for urgent data... 351 */ 352 case DM: 353 break; 354 355 /* 356 * Begin option subnegotiation... 357 */ 358 case SB: 359 state = TS_BEGINNEG; 360 continue; 361 362 case WILL: 363 case WONT: 364 case DO: 365 case DONT: 366 state = TS_WILL + (c - WILL); 367 continue; 368 369 case IAC: 370 *pfrontp++ = c; 371 break; 372 } 373 state = TS_DATA; 374 break; 375 376 case TS_BEGINNEG: 377 if (c == IAC) 378 state = TS_ENDNEG; 379 break; 380 381 case TS_ENDNEG: 382 state = c == SE ? TS_DATA : TS_BEGINNEG; 383 break; 384 385 case TS_WILL: 386 if (!hisopts[c]) 387 willoption(c); 388 state = TS_DATA; 389 continue; 390 391 case TS_WONT: 392 if (hisopts[c]) 393 wontoption(c); 394 state = TS_DATA; 395 continue; 396 397 case TS_DO: 398 if (!myopts[c]) 399 dooption(c); 400 state = TS_DATA; 401 continue; 402 403 case TS_DONT: 404 if (myopts[c]) { 405 myopts[c] = 0; 406 sprintf(nfrontp, wont, c); 407 nfrontp += sizeof (wont) - 2; 408 } 409 state = TS_DATA; 410 continue; 411 412 default: 413 printf("telnetd: panic state=%d\n", state); 414 exit(1); 415 } 416 } 417 } 418 419 willoption(option) 420 int option; 421 { 422 char *fmt; 423 424 switch (option) { 425 426 case TELOPT_BINARY: 427 mode(RAW, 0); 428 goto common; 429 430 case TELOPT_ECHO: 431 mode(0, ECHO|CRMOD); 432 /*FALL THRU*/ 433 434 case TELOPT_SGA: 435 common: 436 hisopts[option] = 1; 437 fmt = doopt; 438 break; 439 440 case TELOPT_TM: 441 fmt = dont; 442 break; 443 444 default: 445 fmt = dont; 446 break; 447 } 448 sprintf(nfrontp, fmt, option); 449 nfrontp += sizeof (dont) - 2; 450 } 451 452 wontoption(option) 453 int option; 454 { 455 char *fmt; 456 457 switch (option) { 458 459 case TELOPT_ECHO: 460 mode(ECHO|CRMOD, 0); 461 goto common; 462 463 case TELOPT_BINARY: 464 mode(0, RAW); 465 /*FALL THRU*/ 466 467 case TELOPT_SGA: 468 common: 469 hisopts[option] = 0; 470 fmt = dont; 471 break; 472 473 default: 474 fmt = dont; 475 } 476 sprintf(nfrontp, fmt, option); 477 nfrontp += sizeof (doopt) - 2; 478 } 479 480 dooption(option) 481 int option; 482 { 483 char *fmt; 484 485 switch (option) { 486 487 case TELOPT_TM: 488 fmt = wont; 489 break; 490 491 case TELOPT_ECHO: 492 mode(ECHO|CRMOD, 0); 493 goto common; 494 495 case TELOPT_BINARY: 496 mode(RAW, 0); 497 /*FALL THRU*/ 498 499 case TELOPT_SGA: 500 common: 501 fmt = will; 502 break; 503 504 default: 505 fmt = wont; 506 break; 507 } 508 sprintf(nfrontp, fmt, option); 509 nfrontp += sizeof (doopt) - 2; 510 } 511 512 mode(on, off) 513 int on, off; 514 { 515 struct sgttyb b; 516 517 ptyflush(); 518 ioctl(pty, TIOCGETP, &b); 519 b.sg_flags |= on; 520 b.sg_flags &= ~off; 521 ioctl(pty, TIOCSETP, &b); 522 } 523 524 /* 525 * Send interrupt to process on other side of pty. 526 * If it is in raw mode, just write NULL; 527 * otherwise, write intr char. 528 */ 529 interrupt() 530 { 531 struct sgttyb b; 532 struct tchars tchars; 533 534 ptyflush(); /* half-hearted */ 535 ioctl(pty, TIOCGETP, &b); 536 if (b.sg_flags & RAW) { 537 *pfrontp++ = '\0'; 538 return; 539 } 540 *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 541 '\177' : tchars.t_intrc; 542 } 543 544 ptyflush() 545 { 546 int n; 547 548 if ((n = pfrontp - pbackp) > 0) 549 n = write(pty, pbackp, n); 550 if (n < 0) 551 return; 552 pbackp += n; 553 if (pbackp == pfrontp) 554 pbackp = pfrontp = ptyobuf; 555 } 556 557 netflush() 558 { 559 int n; 560 561 if ((n = nfrontp - nbackp) > 0) 562 n = write(net, nbackp, n); 563 if (n < 0) { 564 if (errno == EWOULDBLOCK) 565 return; 566 /* should blow this guy away... */ 567 return; 568 } 569 nbackp += n; 570 if (nbackp == nfrontp) 571 nbackp = nfrontp = netobuf; 572 } 573 574 cleanup() 575 { 576 577 rmut(); 578 vhangup(); /* XXX */ 579 shutdown(net, 2); 580 exit(1); 581 } 582 583 #include <utmp.h> 584 585 struct utmp wtmp; 586 char wtmpf[] = "/usr/adm/wtmp"; 587 char utmpf[] = "/etc/utmp"; 588 #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 589 #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 590 591 rmut() 592 { 593 register f; 594 int found = 0; 595 struct utmp *u, *utmp; 596 int nutmp; 597 struct stat statbf; 598 599 f = open(utmpf, O_RDWR); 600 if (f >= 0) { 601 fstat(f, &statbf); 602 utmp = (struct utmp *)malloc(statbf.st_size); 603 if (!utmp) 604 syslog(LOG_ERR, "utmp malloc failed"); 605 if (statbf.st_size && utmp) { 606 nutmp = read(f, utmp, statbf.st_size); 607 nutmp /= sizeof(struct utmp); 608 609 for (u = utmp ; u < &utmp[nutmp] ; u++) { 610 if (SCMPN(u->ut_line, line+5) || 611 u->ut_name[0]==0) 612 continue; 613 lseek(f, ((long)u)-((long)utmp), L_SET); 614 SCPYN(u->ut_name, ""); 615 SCPYN(u->ut_host, ""); 616 time(&u->ut_time); 617 write(f, (char *)u, sizeof(wtmp)); 618 found++; 619 } 620 } 621 close(f); 622 } 623 if (found) { 624 f = open(wtmpf, O_WRONLY|O_APPEND); 625 if (f >= 0) { 626 SCPYN(wtmp.ut_line, line+5); 627 SCPYN(wtmp.ut_name, ""); 628 SCPYN(wtmp.ut_host, ""); 629 time(&wtmp.ut_time); 630 write(f, (char *)&wtmp, sizeof(wtmp)); 631 close(f); 632 } 633 } 634 chmod(line, 0666); 635 chown(line, 0, 0); 636 line[strlen("/dev/")] = 'p'; 637 chmod(line, 0666); 638 chown(line, 0, 0); 639 } 640