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