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