1 /* $NetBSD: sys_term.c,v 1.38 2003/07/14 16:23:40 itojun Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95"; 40 #else 41 __RCSID("$NetBSD: sys_term.c,v 1.38 2003/07/14 16:23:40 itojun Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include "telnetd.h" 46 #include "pathnames.h" 47 48 #include <util.h> 49 50 #include <sys/cdefs.h> 51 52 #include <utmp.h> 53 struct utmp wtmp; 54 55 #define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) 56 #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 57 58 struct termios termbuf, termbuf2; /* pty control structure */ 59 60 void getptyslave __P((void)); 61 int cleanopen __P((char *)); 62 char **addarg __P((char **, char *)); 63 void scrub_env __P((void)); 64 int getent __P((char *, char *)); 65 char *getstr __P((const char *, char **)); 66 #ifdef KRB5 67 extern void kerberos5_cleanup __P((void)); 68 #endif 69 70 /* 71 * init_termbuf() 72 * copy_termbuf(cp) 73 * set_termbuf() 74 * 75 * These three routines are used to get and set the "termbuf" structure 76 * to and from the kernel. init_termbuf() gets the current settings. 77 * copy_termbuf() hands in a new "termbuf" to write to the kernel, and 78 * set_termbuf() writes the structure into the kernel. 79 */ 80 81 void 82 init_termbuf() 83 { 84 (void) tcgetattr(pty, &termbuf); 85 termbuf2 = termbuf; 86 } 87 88 #if defined(LINEMODE) && defined(TIOCPKT_IOCTL) 89 void 90 copy_termbuf(cp, len) 91 char *cp; 92 int len; 93 { 94 if (len > sizeof(termbuf)) 95 len = sizeof(termbuf); 96 memmove((char *)&termbuf, cp, len); 97 termbuf2 = termbuf; 98 } 99 #endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ 100 101 void 102 set_termbuf() 103 { 104 /* 105 * Only make the necessary changes. 106 */ 107 if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) 108 (void) tcsetattr(pty, TCSANOW, &termbuf); 109 } 110 111 112 /* 113 * spcset(func, valp, valpp) 114 * 115 * This function takes various special characters (func), and 116 * sets *valp to the current value of that character, and 117 * *valpp to point to where in the "termbuf" structure that 118 * value is kept. 119 * 120 * It returns the SLC_ level of support for this function. 121 */ 122 123 124 int 125 spcset(func, valp, valpp) 126 int func; 127 cc_t *valp; 128 cc_t **valpp; 129 { 130 131 #define setval(a, b) *valp = termbuf.c_cc[a]; \ 132 *valpp = &termbuf.c_cc[a]; \ 133 return(b); 134 #define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT); 135 136 switch(func) { 137 case SLC_EOF: 138 setval(VEOF, SLC_VARIABLE); 139 case SLC_EC: 140 setval(VERASE, SLC_VARIABLE); 141 case SLC_EL: 142 setval(VKILL, SLC_VARIABLE); 143 case SLC_IP: 144 setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); 145 case SLC_ABORT: 146 setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); 147 case SLC_XON: 148 setval(VSTART, SLC_VARIABLE); 149 case SLC_XOFF: 150 setval(VSTOP, SLC_VARIABLE); 151 case SLC_EW: 152 setval(VWERASE, SLC_VARIABLE); 153 case SLC_RP: 154 setval(VREPRINT, SLC_VARIABLE); 155 case SLC_LNEXT: 156 setval(VLNEXT, SLC_VARIABLE); 157 case SLC_AO: 158 setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); 159 case SLC_SUSP: 160 setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN); 161 case SLC_FORW1: 162 setval(VEOL, SLC_VARIABLE); 163 case SLC_FORW2: 164 setval(VEOL2, SLC_VARIABLE); 165 case SLC_AYT: 166 setval(VSTATUS, SLC_VARIABLE); 167 168 case SLC_BRK: 169 case SLC_SYNCH: 170 case SLC_EOR: 171 defval(0); 172 173 default: 174 *valp = 0; 175 *valpp = 0; 176 return(SLC_NOSUPPORT); 177 } 178 } 179 180 181 /* 182 * getpty() 183 * 184 * Allocate a pty. As a side effect, the external character 185 * array "line" contains the name of the slave side. 186 * 187 * Returns the file descriptor of the opened pty. 188 */ 189 #ifndef __GNUC__ 190 char *line = NULL16STR; 191 #else 192 static char Xline[] = NULL16STR; 193 char *line = Xline; 194 #endif 195 196 197 static int ptyslavefd; /* for cleanopen() */ 198 199 int 200 getpty(ptynum) 201 int *ptynum; 202 { 203 int ptyfd; 204 205 ptyfd = openpty(ptynum, &ptyslavefd, line, NULL, NULL); 206 if (ptyfd == 0) 207 return *ptynum; 208 ptyslavefd = -1; 209 return (-1); 210 } 211 212 #ifdef LINEMODE 213 /* 214 * tty_flowmode() Find out if flow control is enabled or disabled. 215 * tty_linemode() Find out if linemode (external processing) is enabled. 216 * tty_setlinemod(on) Turn on/off linemode. 217 * tty_isecho() Find out if echoing is turned on. 218 * tty_setecho(on) Enable/disable character echoing. 219 * tty_israw() Find out if terminal is in RAW mode. 220 * tty_binaryin(on) Turn on/off BINARY on input. 221 * tty_binaryout(on) Turn on/off BINARY on output. 222 * tty_isediting() Find out if line editing is enabled. 223 * tty_istrapsig() Find out if signal trapping is enabled. 224 * tty_setedit(on) Turn on/off line editing. 225 * tty_setsig(on) Turn on/off signal trapping. 226 * tty_issofttab() Find out if tab expansion is enabled. 227 * tty_setsofttab(on) Turn on/off soft tab expansion. 228 * tty_islitecho() Find out if typed control chars are echoed literally 229 * tty_setlitecho() Turn on/off literal echo of control chars 230 * tty_tspeed(val) Set transmit speed to val. 231 * tty_rspeed(val) Set receive speed to val. 232 */ 233 234 235 int 236 tty_linemode() 237 { 238 return(termbuf.c_lflag & EXTPROC); 239 } 240 241 void 242 tty_setlinemode(on) 243 int on; 244 { 245 set_termbuf(); 246 (void) ioctl(pty, TIOCEXT, (char *)&on); 247 init_termbuf(); 248 } 249 #endif /* LINEMODE */ 250 251 int 252 tty_isecho() 253 { 254 return (termbuf.c_lflag & ECHO); 255 } 256 257 int 258 tty_flowmode() 259 { 260 return((termbuf.c_iflag & IXON) ? 1 : 0); 261 } 262 263 int 264 tty_restartany() 265 { 266 return((termbuf.c_iflag & IXANY) ? 1 : 0); 267 } 268 269 void 270 tty_setecho(on) 271 int on; 272 { 273 if (on) 274 termbuf.c_lflag |= ECHO; 275 else 276 termbuf.c_lflag &= ~ECHO; 277 } 278 279 int 280 tty_israw() 281 { 282 return(!(termbuf.c_lflag & ICANON)); 283 } 284 285 void 286 tty_binaryin(on) 287 int on; 288 { 289 if (on) { 290 termbuf.c_iflag &= ~ISTRIP; 291 } else { 292 termbuf.c_iflag |= ISTRIP; 293 } 294 } 295 296 void 297 tty_binaryout(on) 298 int on; 299 { 300 if (on) { 301 termbuf.c_cflag &= ~(CSIZE|PARENB); 302 termbuf.c_cflag |= CS8; 303 termbuf.c_oflag &= ~OPOST; 304 } else { 305 termbuf.c_cflag &= ~CSIZE; 306 termbuf.c_cflag |= CS7|PARENB; 307 termbuf.c_oflag |= OPOST; 308 } 309 } 310 311 int 312 tty_isbinaryin() 313 { 314 return(!(termbuf.c_iflag & ISTRIP)); 315 } 316 317 int 318 tty_isbinaryout() 319 { 320 return(!(termbuf.c_oflag&OPOST)); 321 } 322 323 #ifdef LINEMODE 324 int 325 tty_isediting() 326 { 327 return(termbuf.c_lflag & ICANON); 328 } 329 330 int 331 tty_istrapsig() 332 { 333 return(termbuf.c_lflag & ISIG); 334 } 335 336 void 337 tty_setedit(on) 338 int on; 339 { 340 if (on) 341 termbuf.c_lflag |= ICANON; 342 else 343 termbuf.c_lflag &= ~ICANON; 344 } 345 346 void 347 tty_setsig(on) 348 int on; 349 { 350 if (on) 351 termbuf.c_lflag |= ISIG; 352 else 353 termbuf.c_lflag &= ~ISIG; 354 } 355 #endif /* LINEMODE */ 356 357 int 358 tty_issofttab() 359 { 360 # ifdef OXTABS 361 return (termbuf.c_oflag & OXTABS); 362 # endif 363 # ifdef TABDLY 364 return ((termbuf.c_oflag & TABDLY) == TAB3); 365 # endif 366 } 367 368 void 369 tty_setsofttab(on) 370 int on; 371 { 372 if (on) { 373 # ifdef OXTABS 374 termbuf.c_oflag |= OXTABS; 375 # endif 376 # ifdef TABDLY 377 termbuf.c_oflag &= ~TABDLY; 378 termbuf.c_oflag |= TAB3; 379 # endif 380 } else { 381 # ifdef OXTABS 382 termbuf.c_oflag &= ~OXTABS; 383 # endif 384 # ifdef TABDLY 385 termbuf.c_oflag &= ~TABDLY; 386 termbuf.c_oflag |= TAB0; 387 # endif 388 } 389 } 390 391 int 392 tty_islitecho() 393 { 394 # ifdef ECHOCTL 395 return (!(termbuf.c_lflag & ECHOCTL)); 396 # endif 397 # ifdef TCTLECH 398 return (!(termbuf.c_lflag & TCTLECH)); 399 # endif 400 # if !defined(ECHOCTL) && !defined(TCTLECH) 401 return (0); /* assumes ctl chars are echoed '^x' */ 402 # endif 403 } 404 405 void 406 tty_setlitecho(on) 407 int on; 408 { 409 # ifdef ECHOCTL 410 if (on) 411 termbuf.c_lflag &= ~ECHOCTL; 412 else 413 termbuf.c_lflag |= ECHOCTL; 414 # endif 415 # ifdef TCTLECH 416 if (on) 417 termbuf.c_lflag &= ~TCTLECH; 418 else 419 termbuf.c_lflag |= TCTLECH; 420 # endif 421 } 422 423 int 424 tty_iscrnl() 425 { 426 return (termbuf.c_iflag & ICRNL); 427 } 428 429 void 430 tty_tspeed(val) 431 int val; 432 { 433 cfsetospeed(&termbuf, val); 434 } 435 436 void 437 tty_rspeed(val) 438 int val; 439 { 440 cfsetispeed(&termbuf, val); 441 } 442 443 444 445 446 /* 447 * getptyslave() 448 * 449 * Open the slave side of the pty, and do any initialization 450 * that is necessary. The return value is a file descriptor 451 * for the slave side. 452 */ 453 extern int def_tspeed, def_rspeed; 454 extern int def_row, def_col; 455 456 void 457 getptyslave() 458 { 459 register int t = -1; 460 461 #ifdef LINEMODE 462 int waslm; 463 #endif 464 struct winsize ws; 465 /* 466 * Opening the slave side may cause initilization of the 467 * kernel tty structure. We need remember the state of 468 * if linemode was turned on 469 * terminal window size 470 * terminal speed 471 * so that we can re-set them if we need to. 472 */ 473 #ifdef LINEMODE 474 waslm = tty_linemode(); 475 #endif 476 477 /* 478 * Make sure that we don't have a controlling tty, and 479 * that we are the session (process group) leader. 480 */ 481 t = open(_PATH_TTY, O_RDWR); 482 if (t >= 0) { 483 (void) ioctl(t, TIOCNOTTY, (char *)0); 484 (void) close(t); 485 } 486 487 488 489 t = cleanopen(line); 490 if (t < 0) 491 fatalperror(net, line); 492 493 494 /* 495 * set up the tty modes as we like them to be. 496 */ 497 init_termbuf(); 498 if (def_row || def_col) { 499 memset((char *)&ws, 0, sizeof(ws)); 500 ws.ws_col = def_col; 501 ws.ws_row = def_row; 502 (void)ioctl(t, TIOCSWINSZ, (char *)&ws); 503 } 504 505 /* 506 * Settings for sgtty based systems 507 */ 508 509 /* 510 * Settings for all other termios/termio based 511 * systems, other than 4.4BSD. In 4.4BSD the 512 * kernel does the initial terminal setup. 513 */ 514 tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); 515 tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); 516 #ifdef LINEMODE 517 if (waslm) 518 tty_setlinemode(1); 519 #endif /* LINEMODE */ 520 521 /* 522 * Set the tty modes, and make this our controlling tty. 523 */ 524 set_termbuf(); 525 if (login_tty(t) == -1) 526 fatalperror(net, "login_tty"); 527 if (net > 2) 528 (void) close(net); 529 if (pty > 2) { 530 (void) close(pty); 531 pty = -1; 532 } 533 } 534 535 /* 536 * Open the specified slave side of the pty, 537 * making sure that we have a clean tty. 538 */ 539 int 540 cleanopen(ttyline) 541 char *ttyline; 542 { 543 return ptyslavefd; 544 } 545 546 /* 547 * startslave(host) 548 * 549 * Given a hostname, do whatever 550 * is necessary to startup the login process on the slave side of the pty. 551 */ 552 553 /* ARGSUSED */ 554 void 555 startslave(host, autologin, autoname) 556 char *host; 557 int autologin; 558 char *autoname; 559 { 560 register int i; 561 562 #ifdef AUTHENTICATION 563 if (!autoname || !autoname[0]) 564 autologin = 0; 565 566 if (autologin < auth_level) { 567 fatal(net, "Authorization failed"); 568 exit(1); 569 } 570 #endif 571 572 573 if ((i = fork()) < 0) 574 fatalperror(net, "fork"); 575 if (i) { 576 } else { 577 getptyslave(); 578 start_login(host, autologin, autoname); 579 /*NOTREACHED*/ 580 } 581 } 582 583 char *envinit[3]; 584 585 void 586 init_env() 587 { 588 char **envp; 589 590 envp = envinit; 591 if ((*envp = getenv("TZ"))) 592 *envp++ -= 3; 593 *envp = 0; 594 environ = envinit; 595 } 596 597 598 /* 599 * start_login(host) 600 * 601 * Assuming that we are now running as a child processes, this 602 * function will turn us into the login process. 603 */ 604 extern char *gettyname; 605 606 void 607 start_login(host, autologin, name) 608 char *host; 609 int autologin; 610 char *name; 611 { 612 register char **argv; 613 #define TABBUFSIZ 512 614 char defent[TABBUFSIZ]; 615 char defstrs[TABBUFSIZ]; 616 #undef TABBUFSIZ 617 const char *loginprog = NULL; 618 619 620 scrub_env(); 621 622 /* 623 * -h : pass on name of host. 624 * WARNING: -h is accepted by login if and only if 625 * getuid() == 0. 626 * -p : don't clobber the environment (so terminal type stays set). 627 * 628 * -f : force this login, he has already been authenticated 629 */ 630 argv = addarg(0, "login"); 631 632 { 633 argv = addarg(argv, "-h"); 634 argv = addarg(argv, host); 635 } 636 argv = addarg(argv, "-p"); 637 #ifdef LINEMODE 638 /* 639 * Set the environment variable "LINEMODE" to either 640 * "real" or "kludge" if we are operating in either 641 * real or kludge linemode. 642 */ 643 if (lmodetype == REAL_LINEMODE) 644 setenv("LINEMODE", "real", 1); 645 # ifdef KLUDGELINEMODE 646 else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) 647 setenv("LINEMODE", "kludge", 1); 648 # endif 649 #endif 650 #ifdef SECURELOGIN 651 /* 652 * don't worry about the -f that might get sent. 653 * A -s is supposed to override it anyhow. 654 */ 655 if (require_secure_login) 656 argv = addarg(argv, "-s"); 657 #endif 658 #ifdef AUTHENTICATION 659 if (auth_level >= 0 && autologin == AUTH_VALID) { 660 argv = addarg(argv, "-f"); 661 argv = addarg(argv, "--"); 662 argv = addarg(argv, name); 663 } else 664 #endif 665 if (getenv("USER")) { 666 argv = addarg(argv, "--"); 667 argv = addarg(argv, getenv("USER")); 668 /* 669 * Assume that login will set the USER variable 670 * correctly. For SysV systems, this means that 671 * USER will no longer be set, just LOGNAME by 672 * login. (The problem is that if the auto-login 673 * fails, and the user then specifies a different 674 * account name, he can get logged in with both 675 * LOGNAME and USER in his environment, but the 676 * USER value will be wrong. 677 */ 678 unsetenv("USER"); 679 } 680 if (getent(defent, gettyname) == 1) { 681 char *cp = defstrs; 682 683 loginprog = getstr("lo", &cp); 684 } 685 if (loginprog == NULL) 686 loginprog = _PATH_LOGIN; 687 closelog(); 688 /* 689 * This sleep(1) is in here so that telnetd can 690 * finish up with the tty. There's a race condition 691 * the login banner message gets lost... 692 */ 693 sleep(1); 694 execv(loginprog, argv); 695 696 syslog(LOG_ERR, "%s: %m", loginprog); 697 fatalperror(net, loginprog); 698 /*NOTREACHED*/ 699 } 700 701 char ** 702 addarg(argv, val) 703 register char **argv; 704 register char *val; 705 { 706 register char **cpp; 707 708 if (argv == NULL) { 709 /* 710 * 10 entries, a leading length, and a null 711 */ 712 argv = (char **)malloc(sizeof(*argv) * 12); 713 if (argv == NULL) 714 return(NULL); 715 *argv++ = (char *)10; 716 *argv = (char *)0; 717 } 718 for (cpp = argv; *cpp; cpp++) 719 ; 720 if (cpp == &argv[(long)argv[-1]]) { 721 --argv; 722 *argv = (char *)((long)(*argv) + 10); 723 argv = (char **)realloc(argv, sizeof(*argv) * ((long)(*argv) + 2)); 724 if (argv == NULL) { 725 fatal(net, "not enough memory"); 726 /*NOTREACHED*/ 727 } 728 argv++; 729 cpp = &argv[(long)argv[-1] - 10]; 730 } 731 *cpp++ = val; 732 *cpp = 0; 733 return(argv); 734 } 735 736 /* 737 * scrub_env() 738 * 739 * We only accept the environment variables listed below. 740 */ 741 742 void 743 scrub_env() 744 { 745 static const char *reject[] = { 746 "TERMCAP=/", 747 NULL 748 }; 749 750 static const char *acceptstr[] = { 751 "XAUTH=", "XAUTHORITY=", "DISPLAY=", 752 "TERM=", 753 "EDITOR=", 754 "PAGER=", 755 "LOGNAME=", 756 "POSIXLY_CORRECT=", 757 "TERMCAP=", 758 "PRINTER=", 759 NULL 760 }; 761 762 char **cpp, **cpp2; 763 const char **p; 764 765 for (cpp2 = cpp = environ; *cpp; cpp++) { 766 int reject_it = 0; 767 768 for(p = reject; *p; p++) 769 if(strncmp(*cpp, *p, strlen(*p)) == 0) { 770 reject_it = 1; 771 break; 772 } 773 if (reject_it) 774 continue; 775 776 for(p = acceptstr; *p; p++) 777 if(strncmp(*cpp, *p, strlen(*p)) == 0) 778 break; 779 if(*p != NULL) 780 *cpp2++ = *cpp; 781 } 782 *cpp2 = NULL; 783 } 784 785 /* 786 * cleanup() 787 * 788 * This is the routine to call when we are all through, to 789 * clean up anything that needs to be cleaned up. 790 */ 791 /* ARGSUSED */ 792 void 793 cleanup(sig) 794 int sig; 795 { 796 char *p, c; 797 798 p = line + sizeof("/dev/") - 1; 799 #ifdef SUPPORT_UTMP 800 if (logout(p)) 801 logwtmp(p, "", ""); 802 #endif 803 #ifdef SUPPORT_UTMPX 804 if (logoutx(p, 0, DEAD_PROCESS)) 805 logwtmpx(p, "", "", 0, DEAD_PROCESS); 806 #endif 807 (void)chmod(line, 0666); 808 (void)chown(line, 0, 0); 809 c = *p; *p = 'p'; 810 (void)chmod(line, 0666); 811 (void)chown(line, 0, 0); 812 *p = c; 813 if (ttyaction(line, "telnetd", "root")) 814 syslog(LOG_ERR, "%s: ttyaction failed", line); 815 (void) shutdown(net, 2); 816 exit(1); 817 } 818