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