1 /* $NetBSD: main.c,v 1.67 2020/02/26 15:44:57 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 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 34 #ifndef lint 35 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 36 The Regents of the University of California. All rights reserved."); 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "from: @(#)main.c 8.1 (Berkeley) 6/20/93"; 42 #else 43 __RCSID("$NetBSD: main.c,v 1.67 2020/02/26 15:44:57 riastradh Exp $"); 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/ioctl.h> 49 #include <sys/resource.h> 50 #include <sys/stat.h> 51 #include <sys/utsname.h> 52 53 #include <ctype.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <limits.h> 57 #include <pwd.h> 58 #include <setjmp.h> 59 #include <signal.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <syslog.h> 64 #include <term.h> 65 #include <termios.h> 66 #include <time.h> 67 #include <ttyent.h> 68 #include <unistd.h> 69 #include <util.h> 70 71 #include "gettytab.h" 72 #include "pathnames.h" 73 #include "extern.h" 74 75 extern char editedhost[]; 76 77 /* 78 * Set the amount of running time that getty should accumulate 79 * before deciding that something is wrong and exit. 80 */ 81 #define GETTY_TIMEOUT 60 /* seconds */ 82 83 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */ 84 85 #define PPP_FRAME 0x7e /* PPP Framing character */ 86 #define PPP_STATION 0xff /* "All Station" character */ 87 #define PPP_ESCAPE 0x7d /* Escape Character */ 88 #define PPP_CONTROL 0x03 /* PPP Control Field */ 89 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */ 90 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */ 91 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */ 92 93 struct termios tmode, omode; 94 95 int crmod, digit_or_punc, lower, upper; 96 97 char hostname[MAXHOSTNAMELEN + 1]; 98 struct utsname kerninfo; 99 char name[LOGIN_NAME_MAX]; 100 char dev[] = _PATH_DEV; 101 char ttyn[32]; 102 uid_t ttyowner = 0; 103 char *rawttyn; 104 105 #define OBUFSIZ 128 106 #define TABBUFSIZ 512 107 108 char defent[TABBUFSIZ]; 109 char tabent[TABBUFSIZ]; 110 111 char *env[128]; 112 113 const unsigned char partab[] = { 114 0001,0201,0201,0001,0201,0001,0001,0201, 115 0202,0004,0003,0205,0005,0206,0201,0001, 116 0201,0001,0001,0201,0001,0201,0201,0001, 117 0001,0201,0201,0001,0201,0001,0001,0201, 118 0200,0000,0000,0200,0000,0200,0200,0000, 119 0000,0200,0200,0000,0200,0000,0000,0200, 120 0000,0200,0200,0000,0200,0000,0000,0200, 121 0200,0000,0000,0200,0000,0200,0200,0000, 122 0200,0000,0000,0200,0000,0200,0200,0000, 123 0000,0200,0200,0000,0200,0000,0000,0200, 124 0000,0200,0200,0000,0200,0000,0000,0200, 125 0200,0000,0000,0200,0000,0200,0200,0000, 126 0000,0200,0200,0000,0200,0000,0000,0200, 127 0200,0000,0000,0200,0000,0200,0200,0000, 128 0200,0000,0000,0200,0000,0200,0200,0000, 129 0000,0200,0200,0000,0200,0000,0000,0201 130 }; 131 132 #define ERASE tmode.c_cc[VERASE] 133 #define KILL tmode.c_cc[VKILL] 134 #define EOT tmode.c_cc[VEOF] 135 136 static void clearscreen(void); 137 138 sigjmp_buf timeout; 139 140 __dead static void 141 /*ARGSUSED*/ 142 dingdong(int signo) 143 { 144 145 (void)alarm(0); 146 (void)signal(SIGALRM, SIG_DFL); 147 siglongjmp(timeout, 1); 148 } 149 150 sigjmp_buf intrupt; 151 152 __dead static void 153 /*ARGSUSED*/ 154 interrupt(int signo) 155 { 156 157 (void)signal(SIGINT, interrupt); 158 siglongjmp(intrupt, 1); 159 } 160 161 /* 162 * Action to take when getty is running too long. 163 */ 164 __dead static void 165 /*ARGSUSED*/ 166 timeoverrun(int signo) 167 { 168 169 syslog(LOG_ERR, "getty exiting due to excessive running time"); 170 exit(1); 171 } 172 173 static int getname(void); 174 static void oflush(void); 175 static void prompt(void); 176 static int putchr(int); 177 static void putf(const char *); 178 static void xputs(const char *); 179 180 #define putpad(s) tputs(s, 1, putchr) 181 182 int 183 main(int argc, char *argv[], char *envp[]) 184 { 185 int repcnt = 0, failopenlogged = 0; 186 volatile int first_time = 1; 187 struct rlimit limit; 188 int rval; 189 /* this is used past the siglongjmp, so make sure it is not cached 190 in registers that might become invalid. */ 191 const char * volatile tname = "default"; 192 193 (void)signal(SIGINT, SIG_IGN); 194 openlog("getty", LOG_PID, LOG_AUTH); 195 (void)gethostname(hostname, sizeof(hostname)); 196 hostname[sizeof(hostname) - 1] = '\0'; 197 if (hostname[0] == '\0') 198 (void)strlcpy(hostname, "Amnesiac", sizeof(hostname)); 199 (void)uname(&kerninfo); 200 201 /* 202 * Limit running time to deal with broken or dead lines. 203 */ 204 (void)signal(SIGXCPU, timeoverrun); 205 limit.rlim_max = RLIM_INFINITY; 206 limit.rlim_cur = GETTY_TIMEOUT; 207 (void)setrlimit(RLIMIT_CPU, &limit); 208 209 /* 210 * The following is a work around for vhangup interactions 211 * which cause great problems getting window systems started. 212 * If the tty line is "-", we do the old style getty presuming 213 * that the file descriptors are already set up for us. 214 * J. Gettys - MIT Project Athena. 215 */ 216 if (argc <= 2 || strcmp(argv[2], "-") == 0) { 217 (void)strlcpy(ttyn, ttyname(0), sizeof(ttyn)); 218 } 219 else { 220 int i; 221 222 rawttyn = argv[2]; 223 (void)strlcpy(ttyn, dev, sizeof(ttyn)); 224 (void)strlcat(ttyn, argv[2], sizeof(ttyn)); 225 if (strcmp(argv[0], "+") != 0) { 226 (void)chown(ttyn, ttyowner, 0); 227 (void)chmod(ttyn, 0600); 228 (void)revoke(ttyn); 229 if (ttyaction(ttyn, "getty", "root")) 230 syslog(LOG_WARNING, "%s: ttyaction failed", 231 ttyn); 232 /* 233 * Delay the open so DTR stays down long enough 234 * to be detected. 235 */ 236 (void)sleep(2); 237 while ((i = open(ttyn, O_RDWR)) == -1) { 238 if ((repcnt % 10 == 0) && 239 (errno != ENXIO || !failopenlogged)) { 240 syslog(LOG_WARNING, "%s: %m", ttyn); 241 closelog(); 242 failopenlogged = 1; 243 } 244 repcnt++; 245 (void)sleep(60); 246 } 247 (void)login_tty(i); 248 } 249 } 250 251 /* Start with default tty settings */ 252 if (tcgetattr(0, &tmode) < 0) { 253 syslog(LOG_ERR, "%s: %m", ttyn); 254 exit(1); 255 } 256 omode = tmode; 257 258 gettable("default", defent); 259 gendefaults(); 260 if (argc > 1) 261 tname = argv[1]; 262 for (;;) { 263 int off; 264 265 rval = 0; 266 gettable(tname, tabent); 267 if (OPset || EPset || APset) 268 APset++, OPset++, EPset++; 269 setdefaults(); 270 off = 0; 271 (void)tcflush(0, TCIOFLUSH); /* clear out the crap */ 272 (void)ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ 273 (void)ioctl(0, FIOASYNC, &off); /* ditto for async mode */ 274 275 if (IS) 276 (void)cfsetispeed(&tmode, (speed_t)IS); 277 else if (SP) 278 (void)cfsetispeed(&tmode, (speed_t)SP); 279 if (OS) 280 (void)cfsetospeed(&tmode, (speed_t)OS); 281 else if (SP) 282 (void)cfsetospeed(&tmode, (speed_t)SP); 283 setflags(0); 284 setchars(); 285 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 286 syslog(LOG_ERR, "%s: %m", ttyn); 287 exit(1); 288 } 289 if (AB) { 290 tname = autobaud(); 291 continue; 292 } 293 if (PS) { 294 tname = portselector(); 295 continue; 296 } 297 if (CS) 298 clearscreen(); 299 if (CL && *CL) 300 putpad(CL); 301 edithost(HE); 302 303 /* 304 * If this is the first time through this, and an 305 * issue file has been given, then send it. 306 */ 307 if (first_time != 0 && IF != NULL) { 308 char buf[_POSIX2_LINE_MAX]; 309 FILE *fp; 310 311 if ((fp = fopen(IF, "r")) != NULL) { 312 while (fgets(buf, sizeof(buf) - 1, fp) != NULL) 313 putf(buf); 314 (void)fclose(fp); 315 } 316 } 317 first_time = 0; 318 319 if (IM && *IM) 320 putf(IM); 321 oflush(); 322 if (sigsetjmp(timeout, 1)) { 323 tmode.c_ispeed = tmode.c_ospeed = 0; 324 (void)tcsetattr(0, TCSANOW, &tmode); 325 exit(1); 326 } 327 if (TO) { 328 (void)signal(SIGALRM, dingdong); 329 (void)alarm((unsigned int)TO); 330 } 331 if (NN) { 332 name[0] = '\0'; 333 lower = 1; 334 upper = digit_or_punc = 0; 335 } else if (AL) { 336 const char *p = AL; 337 char *q = name; 338 339 while (*p && q < &name[sizeof name - 1]) { 340 if (isupper((unsigned char)*p)) 341 upper = 1; 342 else if (islower((unsigned char)*p)) 343 lower = 1; 344 else if (isdigit((unsigned char)*p)) 345 digit_or_punc = 1; 346 *q++ = *p++; 347 } 348 } else if ((rval = getname()) == 2) { 349 setflags(2); 350 (void)execle(PP, "ppplogin", ttyn, (char *) 0, env); 351 syslog(LOG_ERR, "%s: %m", PP); 352 exit(1); 353 } 354 355 if (rval || AL || NN) { 356 int i; 357 358 oflush(); 359 (void)alarm(0); 360 (void)signal(SIGALRM, SIG_DFL); 361 if (name[0] == '-') { 362 xputs("user names may not start with '-'."); 363 continue; 364 } 365 if (!(upper || lower || digit_or_punc)) 366 continue; 367 setflags(2); 368 if (crmod) { 369 tmode.c_iflag |= ICRNL; 370 tmode.c_oflag |= ONLCR; 371 } 372 #if XXX 373 if (upper || UC) 374 tmode.sg_flags |= LCASE; 375 if (lower || LC) 376 tmode.sg_flags &= ~LCASE; 377 #endif 378 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 379 syslog(LOG_ERR, "%s: %m", ttyn); 380 exit(1); 381 } 382 (void)signal(SIGINT, SIG_DFL); 383 for (i = 0; envp[i] != NULL; i++) 384 env[i] = envp[i]; 385 makeenv(&env[i]); 386 387 limit.rlim_max = RLIM_INFINITY; 388 limit.rlim_cur = RLIM_INFINITY; 389 (void)setrlimit(RLIMIT_CPU, &limit); 390 if (NN) 391 (void)execle(LO, "login", AL ? "-fp" : "-p", 392 NULL, env); 393 else 394 (void)execle(LO, "login", AL ? "-fp" : "-p", 395 "--", name, NULL, env); 396 syslog(LOG_ERR, "%s: %m", LO); 397 exit(1); 398 } 399 (void)alarm(0); 400 (void)signal(SIGALRM, SIG_DFL); 401 (void)signal(SIGINT, SIG_IGN); 402 if (NX && *NX) 403 tname = NX; 404 } 405 } 406 407 static int 408 getname(void) 409 { 410 int c; 411 char *np; 412 unsigned char cs; 413 int ppp_state, ppp_connection; 414 415 /* 416 * Interrupt may happen if we use CBREAK mode 417 */ 418 if (sigsetjmp(intrupt, 1)) { 419 (void)signal(SIGINT, SIG_IGN); 420 return (0); 421 } 422 (void)signal(SIGINT, interrupt); 423 setflags(1); 424 prompt(); 425 if (PF > 0) { 426 oflush(); 427 (void)sleep((unsigned int)PF); 428 PF = 0; 429 } 430 if (tcsetattr(0, TCSANOW, &tmode) < 0) { 431 syslog(LOG_ERR, "%s: %m", ttyn); 432 exit(1); 433 } 434 crmod = digit_or_punc = lower = upper = 0; 435 ppp_state = ppp_connection = 0; 436 np = name; 437 for (;;) { 438 oflush(); 439 if (read(STDIN_FILENO, &cs, 1) <= 0) 440 exit(0); 441 if ((c = cs&0177) == 0) 442 return (0); 443 444 /* 445 * PPP detection state machine.. 446 * Look for sequences: 447 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or 448 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) 449 * See RFC1662. 450 * Derived from code from Michael Hancock <michaelh@cet.co.jp> 451 * and Erik 'PPP' Olson <eriko@wrq.com> 452 */ 453 if (PP && cs == PPP_FRAME) { 454 ppp_state = 1; 455 } else if (ppp_state == 1 && cs == PPP_STATION) { 456 ppp_state = 2; 457 } else if (ppp_state == 2 && cs == PPP_ESCAPE) { 458 ppp_state = 3; 459 } else if ((ppp_state == 2 && cs == PPP_CONTROL) || 460 (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { 461 ppp_state = 4; 462 } else if (ppp_state == 4 && cs == PPP_LCP_HI) { 463 ppp_state = 5; 464 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { 465 ppp_connection = 1; 466 break; 467 } else { 468 ppp_state = 0; 469 } 470 471 if (c == EOT) 472 exit(1); 473 if (c == '\r' || c == '\n' || 474 np >= &name[LOGIN_NAME_MAX - 1]) { 475 *np = '\0'; 476 putf("\r\n"); 477 break; 478 } 479 if (islower(c)) 480 lower = 1; 481 else if (isupper(c)) 482 upper = 1; 483 else if (c == ERASE || c == '#' || c == '\b') { 484 if (np > name) { 485 np--; 486 if (cfgetospeed(&tmode) >= 1200) 487 xputs("\b \b"); 488 else 489 putchr(cs); 490 } 491 continue; 492 } else if (c == KILL || c == '@') { 493 putchr(cs); 494 putchr('\r'); 495 if (cfgetospeed(&tmode) < 1200) 496 putchr('\n'); 497 /* this is the way they do it down under ... */ 498 else if (np > name) 499 xputs( 500 " \r"); 501 prompt(); 502 np = name; 503 continue; 504 } else if (isdigit(c) || c == '_') 505 digit_or_punc = 1; 506 if (IG && (c <= ' ' || c > 0176)) 507 continue; 508 *np++ = c; 509 putchr(cs); 510 511 /* 512 * An MS-Windows direct connect PPP "client" won't send its 513 * first PPP packet until we respond to its "CLIENT" poll 514 * with a CRLF sequence. We cater to yet another broken 515 * implementation of a previously-standard protocol... 516 */ 517 *np = '\0'; 518 if (strstr(name, "CLIENT")) 519 putf("\r\n"); 520 } 521 (void)signal(SIGINT, SIG_IGN); 522 *np = 0; 523 if (c == '\r') 524 crmod = 1; 525 if ((upper && !lower && !LC) || UC) 526 for (np = name; *np; np++) 527 *np = tolower((unsigned char)*np); 528 return (1 + ppp_connection); 529 } 530 531 static void 532 xputs(const char *s) 533 { 534 while (*s) 535 putchr(*s++); 536 } 537 538 char outbuf[OBUFSIZ]; 539 size_t obufcnt = 0; 540 541 static int 542 putchr(int cc) 543 { 544 unsigned char c; 545 546 c = cc; 547 if (!NP) { 548 c |= partab[c&0177] & 0200; 549 if (OP) 550 c ^= 0200; 551 } 552 if (!UB) { 553 outbuf[obufcnt++] = c; 554 if (obufcnt >= OBUFSIZ) 555 oflush(); 556 return 1; 557 } 558 return write(STDOUT_FILENO, &c, 1); 559 } 560 561 static void 562 oflush(void) 563 { 564 if (obufcnt) 565 (void)write(STDOUT_FILENO, outbuf, obufcnt); 566 obufcnt = 0; 567 } 568 569 static void 570 prompt(void) 571 { 572 573 putf(LM); 574 if (CO) 575 putchr('\n'); 576 } 577 578 static void 579 putf(const char *cp) 580 { 581 time_t t; 582 char *slash, db[100]; 583 584 while (*cp) { 585 if (*cp != '%') { 586 putchr(*cp++); 587 continue; 588 } 589 switch (*++cp) { 590 591 case 't': 592 if ((slash = strstr(ttyn, "/pts/")) == NULL) 593 slash = strrchr(ttyn, '/'); 594 if (slash == NULL) 595 xputs(ttyn); 596 else 597 xputs(&slash[1]); 598 break; 599 600 case 'h': 601 xputs(editedhost); 602 break; 603 604 case 'd': 605 (void)time(&t); 606 (void)strftime(db, sizeof(db), 607 "%l:%M%p on %A, %d %B %Y", localtime(&t)); 608 xputs(db); 609 break; 610 611 case 's': 612 xputs(kerninfo.sysname); 613 break; 614 615 case 'm': 616 xputs(kerninfo.machine); 617 break; 618 619 case 'r': 620 xputs(kerninfo.release); 621 break; 622 623 case 'v': 624 xputs(kerninfo.version); 625 break; 626 627 case '%': 628 putchr('%'); 629 break; 630 } 631 if (*cp) 632 cp++; 633 } 634 } 635 636 static void 637 clearscreen(void) 638 { 639 struct ttyent *typ; 640 int err; 641 642 if (rawttyn == NULL) 643 return; 644 645 typ = getttynam(rawttyn); 646 647 if ((typ == NULL) || (typ->ty_type == NULL) || 648 (typ->ty_type[0] == 0)) 649 return; 650 651 if (setupterm(typ->ty_type, 0, &err) == ERR) 652 return; 653 654 if (clear_screen) 655 putpad(clear_screen); 656 657 del_curterm(cur_term); 658 cur_term = NULL; 659 } 660