1 /* $NetBSD: sys_term.c,v 1.50 2024/10/29 13:10:10 kre 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.50 2024/10/29 13:10:10 kre 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 char *user; 587 588 user = getenv("USER"); 589 user = (user != NULL) ? strdup(user) : NULL; 590 591 scrub_env(); 592 593 /* 594 * -a : pass on the address of the host. 595 * -h : pass on name of host. 596 * WARNING: -h and -a are accepted by login 597 * if and only if getuid() == 0. 598 * -p : don't clobber the environment (so terminal type stays set). 599 * 600 * -f : force this login, he has already been authenticated 601 */ 602 argv = addarg(0, "login"); 603 604 argv = addarg(argv, "-a"); 605 (void)strvisx(buf, (const char *)(const void *)&from, sizeof(from), 606 VIS_WHITE); 607 argv = addarg(argv, buf); 608 609 argv = addarg(argv, "-h"); 610 argv = addarg(argv, host); 611 612 argv = addarg(argv, "-p"); 613 #ifdef LINEMODE 614 /* 615 * Set the environment variable "LINEMODE" to either 616 * "real" or "kludge" if we are operating in either 617 * real or kludge linemode. 618 */ 619 if (lmodetype == REAL_LINEMODE) 620 setenv("LINEMODE", "real", 1); 621 # ifdef KLUDGELINEMODE 622 else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) 623 setenv("LINEMODE", "kludge", 1); 624 # endif 625 #endif 626 #ifdef SECURELOGIN 627 /* 628 * don't worry about the -f that might get sent. 629 * A -s is supposed to override it anyhow. 630 */ 631 if (require_secure_login) 632 argv = addarg(argv, "-s"); 633 #endif 634 #ifdef AUTHENTICATION 635 if (auth_level >= 0 && autologin == AUTH_VALID) { 636 argv = addarg(argv, "-f"); 637 argv = addarg(argv, "--"); 638 argv = addarg(argv, name); 639 } else 640 #endif 641 if (user != NULL) { 642 argv = addarg(argv, "--"); 643 argv = addarg(argv, user); 644 /* 645 * Assume that login will set the USER variable 646 * correctly. For SysV systems, this means that 647 * USER will no longer be set, just LOGNAME by 648 * login. (The problem is that if the auto-login 649 * fails, and the user then specifies a different 650 * account name, he can get logged in with both 651 * LOGNAME and USER in his environment, but the 652 * USER value will be wrong. 653 */ 654 unsetenv("USER"); 655 } 656 if (getent(defent, gettyname) == 1) { 657 char *cp = defstrs; 658 659 loginprog = getstr("lo", &cp); 660 } 661 if (loginprog == NULL) 662 loginprog = _PATH_LOGIN; 663 closelog(); 664 /* 665 * This sleep(1) is in here so that telnetd can 666 * finish up with the tty. There's a race condition 667 * the login banner message gets lost... 668 */ 669 sleep(1); 670 execv(loginprog, argv); 671 672 syslog(LOG_ERR, "%s: %m", loginprog); 673 fatalperror(net, loginprog); 674 /*NOTREACHED*/ 675 } 676 677 char ** 678 addarg(char **argv, const char *val) 679 { 680 char **cpp; 681 char **nargv; 682 683 if (argv == NULL) { 684 /* 685 * 10 entries, a leading length, and a null 686 */ 687 argv = malloc(sizeof(*argv) * 12); 688 if (argv == NULL) 689 return(NULL); 690 *argv++ = (char *)10; 691 *argv = (char *)0; 692 } 693 for (cpp = argv; *cpp; cpp++) 694 ; 695 if (cpp == &argv[(long)argv[-1]]) { 696 --argv; 697 nargv = realloc(argv, sizeof(*argv) * ((long)(*argv) + 10 + 2)); 698 if (nargv == NULL) { 699 fatal(net, "not enough memory"); 700 /*NOTREACHED*/ 701 } 702 argv = nargv; 703 *argv = (char *)((long)(*argv) + 10); 704 argv++; 705 cpp = &argv[(long)argv[-1] - 10]; 706 } 707 *cpp++ = __UNCONST(val); 708 *cpp = 0; 709 return(argv); 710 } 711 712 /* 713 * scrub_env() 714 * 715 * We only accept the environment variables listed below. 716 */ 717 718 void 719 scrub_env(void) 720 { 721 static const char *reject[] = { 722 "TERMCAP=/", 723 NULL 724 }; 725 726 static const char *acceptstr[] = { 727 "XAUTH=", "XAUTHORITY=", "DISPLAY=", 728 "TERM=", 729 "EDITOR=", 730 "PAGER=", 731 "LOGNAME=", 732 "POSIXLY_CORRECT=", 733 "TERMCAP=", 734 "PRINTER=", 735 NULL 736 }; 737 738 char **cpp, **cpp2; 739 const char **p; 740 741 for (cpp2 = cpp = environ; *cpp; cpp++) { 742 int reject_it = 0; 743 744 for(p = reject; *p; p++) 745 if(strncmp(*cpp, *p, strlen(*p)) == 0) { 746 reject_it = 1; 747 break; 748 } 749 if (reject_it) 750 continue; 751 752 for(p = acceptstr; *p; p++) 753 if(strncmp(*cpp, *p, strlen(*p)) == 0) 754 break; 755 if(*p != NULL) 756 *cpp2++ = *cpp; 757 } 758 *cpp2 = NULL; 759 } 760 761 /* 762 * cleanup() 763 * 764 * This is the routine to call when we are all through, to 765 * clean up anything that needs to be cleaned up. 766 */ 767 /* ARGSUSED */ 768 void 769 cleanup(int sig) 770 { 771 char *p, c; 772 773 p = line + sizeof(_PATH_DEV) - 1; 774 #ifdef SUPPORT_UTMP 775 if (logout(p)) 776 logwtmp(p, "", ""); 777 #endif 778 #ifdef SUPPORT_UTMPX 779 if (logoutx(p, 0, DEAD_PROCESS)) 780 logwtmpx(p, "", "", 0, DEAD_PROCESS); 781 #endif 782 (void)chmod(line, 0666); 783 (void)chown(line, 0, 0); 784 c = *p; *p = 'p'; 785 (void)chmod(line, 0666); 786 (void)chown(line, 0, 0); 787 *p = c; 788 if (ttyaction(line, "telnetd", "root")) 789 syslog(LOG_ERR, "%s: ttyaction failed", line); 790 (void) shutdown(net, 2); 791 exit(1); 792 } 793