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